@startuml 이벤트생성플로우 !theme mono title 이벤트 생성 플로우 - 외부 시퀀스 다이어그램 actor "소상공인" as User participant "Frontend" as FE participant "API Gateway" as Gateway participant "Event Service" as Event participant "AI Service" as AI participant "Content Service" as Content participant "Distribution Service" as Dist participant "Kafka" as Kafka participant "Redis Cache" as Cache database "Event DB" as EventDB participant "외부 AI API" as AIApi participant "이미지 생성 API" as ImageApi participant "배포 채널 APIs" as ChannelApis == 1. 이벤트 목적 선택 (UFR-EVENT-020) == User -> FE: 이벤트 목적 선택 FE -> Gateway: POST /events/purposes\n{목적, 매장정보} Gateway -> Event: 이벤트 목적 저장 요청 Event -> Cache: 캐시 조회\nkey: purpose:{userId} alt 캐시 히트 Cache --> Event: 캐시된 데이터 else 캐시 미스 Event -> EventDB: 이벤트 목적 저장 EventDB --> Event: 저장 완료 Event -> Cache: 캐시 저장\nTTL: 30분 end Event --> Gateway: 저장 완료\n{eventDraftId} Gateway --> FE: 200 OK FE --> User: AI 추천 화면으로 이동 == 2. AI 이벤트 추천 - 비동기 처리 (UFR-EVENT-030) == User -> FE: AI 추천 요청 FE -> Gateway: POST /api/events/{eventDraftId}/ai-recommendations\n{목적, 업종, 지역} Gateway -> Event: AI 추천 요청 전달 Event -> Kafka: Publish to ai-job-topic\n{jobId, eventDraftId, 목적, 업종, 지역} Event --> Gateway: Job 생성 완료\n{jobId, status: PENDING} Gateway --> FE: 202 Accepted\n{jobId} FE --> User: "AI가 분석 중입니다..." (로딩) note over AI: Kafka Consumer\nai-job-topic 구독 Kafka --> AI: Consume Job Message\n{jobId, eventDraftId, ...} AI -> Cache: 트렌드 분석 캐시 조회\nkey: trend:{업종}:{지역} alt 캐시 히트 Cache --> AI: 캐시된 트렌드 데이터 else 캐시 미스 AI -> EventDB: 과거 이벤트 데이터 조회 EventDB --> AI: 이벤트 통계 데이터 AI -> AIApi: 트렌드 분석 요청\n{업종, 지역, 과거데이터} AIApi --> AI: 트렌드 분석 결과 AI -> Cache: 트렌드 캐시 저장\nTTL: 1시간 end AI -> AIApi: 이벤트 추천 요청\n{목적, 트렌드, 매장정보} AIApi --> AI: 3가지 추천안 생성 AI -> EventDB: 추천 결과 저장 EventDB --> AI: 저장 완료 AI -> Cache: Job 상태 업데이트\nkey: job:{jobId}\nstatus: COMPLETED AI -> Kafka: Publish to event-topic\nEventRecommended\n{jobId, eventDraftId, recommendations} group Polling으로 상태 확인 loop 상태 확인 (최대 30초) FE -> Gateway: GET /jobs/{jobId}/status Gateway -> Event: Job 상태 조회 Event -> Cache: 캐시에서 Job 상태 확인 Cache --> Event: {status, result} alt Job 완료 Event --> Gateway: 200 OK\n{status: COMPLETED, recommendations} Gateway --> FE: 추천 결과 반환 FE --> User: 3가지 추천안 표시\n(제목/경품 수정 가능) else Job 진행중 Event --> Gateway: 200 OK\n{status: PENDING/PROCESSING} Gateway --> FE: 진행중 상태 note over FE: 2초 후 재요청 end end end User -> FE: 추천안 선택\n(제목/경품 커스텀) FE -> Gateway: PUT /events/drafts/{eventDraftId}\n{선택한 추천안, 커스텀 정보} Gateway -> Event: 선택 저장 Event -> EventDB: 이벤트 초안 업데이트 EventDB --> Event: 업데이트 완료 Event --> Gateway: 200 OK Gateway --> FE: 저장 완료 FE --> User: 콘텐츠 생성 화면으로 이동 == 3. SNS 이미지 생성 - 비동기 처리 (UFR-CONT-010) == User -> FE: 이미지 생성 요청 FE -> Gateway: POST /api/events/{eventDraftId}/content-generation\n{이벤트정보} Gateway -> Event: 이미지 생성 요청 Event -> Kafka: Publish to image-job-topic\n{jobId, eventDraftId, 이벤트정보} Event --> Gateway: Job 생성 완료\n{jobId, status: PENDING} Gateway --> FE: 202 Accepted\n{jobId} FE --> User: "이미지 생성 중..." (로딩) note over Content: Kafka Consumer\nimage-job-topic 구독 Kafka --> Content: Consume Job Message\n{jobId, eventDraftId, ...} par 심플 스타일 Content -> ImageApi: 심플 스타일 생성 요청 ImageApi --> Content: 심플 이미지 URL else 화려한 스타일 Content -> ImageApi: 화려한 스타일 생성 요청 ImageApi --> Content: 화려한 이미지 URL else 트렌디 스타일 Content -> ImageApi: 트렌디 스타일 생성 요청 ImageApi --> Content: 트렌디 이미지 URL end Content -> EventDB: 이미지 URL 저장 EventDB --> Content: 저장 완료 Content -> Cache: Job 상태 업데이트\nkey: job:{jobId}\nstatus: COMPLETED Content -> Kafka: Publish to event-topic\nContentCreated\n{jobId, eventDraftId, imageUrls} group Polling으로 상태 확인 loop 상태 확인 (최대 30초) FE -> Gateway: GET /jobs/{jobId}/status Gateway -> Event: Job 상태 조회 Event -> Cache: 캐시에서 Job 상태 확인 Cache --> Event: {status, imageUrls} alt Job 완료 Event --> Gateway: 200 OK\n{status: COMPLETED, imageUrls} Gateway --> FE: 이미지 URL 반환 FE --> User: 3가지 스타일 카드 표시 else Job 진행중 Event --> Gateway: 200 OK\n{status: PENDING/PROCESSING} Gateway --> FE: 진행중 상태 note over FE: 2초 후 재요청 end end end User -> FE: 스타일 선택 및 편집 FE -> Gateway: PUT /events/drafts/{eventDraftId}/content\n{선택한 이미지, 편집내용} Gateway -> Event: 콘텐츠 선택 저장 Event -> EventDB: 이벤트 초안 업데이트 EventDB --> Event: 업데이트 완료 Event --> Gateway: 200 OK Gateway --> FE: 저장 완료 FE --> User: 배포 채널 선택 화면으로 이동 == 4. 최종 승인 및 다중 채널 배포 - 동기 처리 (UFR-EVENT-050) == User -> FE: 배포 채널 선택\n최종 승인 요청 FE -> Gateway: POST /api/events/{eventDraftId}/publish\n{선택 채널 목록} Gateway -> Event: 최종 승인 및 배포 처리 Event -> EventDB: 이벤트 상태 변경\nDRAFT → APPROVED EventDB --> Event: 상태 변경 완료 Event -> Kafka: Publish to event-topic\nEventCreated\n{eventId, 이벤트정보} note over Event: 동기 호출로 배포 진행 Event -> Dist: REST API - 배포 요청\nPOST /distributions\n{eventId, channels, content} note over Dist: Circuit Breaker 적용 par 우리동네TV alt 우리동네TV 선택 Dist -> ChannelApis: 우리동네TV API\n15초 영상 업로드 ChannelApis --> Dist: 배포 완료\n{배포ID, 예상노출수} end else 링고비즈 alt 링고비즈 선택 Dist -> ChannelApis: 링고비즈 API\n연결음 업데이트 ChannelApis --> Dist: 업데이트 완료\n{완료시각} end else 지니TV alt 지니TV 선택 Dist -> ChannelApis: 지니TV API\n광고 등록 ChannelApis --> Dist: 광고 등록 완료\n{광고ID, 스케줄} end else Instagram alt Instagram 선택 Dist -> ChannelApis: Instagram API\n포스팅 ChannelApis --> Dist: 포스팅 완료\n{postUrl} end else Naver Blog alt Naver Blog 선택 Dist -> ChannelApis: Naver API\n블로그 포스팅 ChannelApis --> Dist: 포스팅 완료\n{postUrl} end else Kakao Channel alt Kakao Channel 선택 Dist -> ChannelApis: Kakao API\n채널 포스팅 ChannelApis --> Dist: 포스팅 완료\n{postUrl} end end Dist -> EventDB: 배포 이력 저장 EventDB --> Dist: 저장 완료 Dist -> Kafka: Publish to event-topic\nDistributionCompleted\n{eventId, 배포결과} Dist --> Event: REST API 응답\n200 OK\n{배포결과, 채널별 상태} Event -> EventDB: 이벤트 상태 업데이트\nAPPROVED → ACTIVE EventDB --> Event: 업데이트 완료 Event --> Gateway: 200 OK\n{eventId, 배포결과} Gateway --> FE: 배포 완료 FE --> User: "이벤트가 배포되었습니다"\n대시보드로 이동 note over Event, Dist: 배포 실패 시\n- 채널별 독립 처리\n- 자동 재시도 (최대 3회)\n- Circuit Breaker로 장애 전파 방지\n- 실패한 채널만 재시도 가능 @enduml