diff --git a/design/backend/sequence/inner/content-이미지생성.puml b/design/backend/sequence/inner/content-이미지생성.puml index ce39e02..5a386fa 100644 --- a/design/backend/sequence/inner/content-이미지생성.puml +++ b/design/backend/sequence/inner/content-이미지생성.puml @@ -202,20 +202,12 @@ else 캐시 MISS (새로운 이미지 생성) JobStatus --> Handler: 업데이트 완료 deactivate JobStatus - == Kafka 이벤트 발행 (이미지 생성 완료) == - note over Handler: Kafka 이벤트 발행하여\nEvent Service에 결과 전달 - - Handler -> Consumer: Kafka 이벤트 발행 - activate Consumer - Consumer -> Consumer: Kafka Producer로\nevent-topic 발행\nContentCreated\n{jobId, eventDraftId, imageUrls} - note right - Event Service는 Kafka Consumer로 - ContentCreated 이벤트를 구독하여 - Event DB에 이미지 URL 저장 - end note - deactivate Consumer - Handler --> Consumer: 처리 완료 + note over Handler + 이미지 URL은 Redis에만 저장됨 + Event Service는 폴링을 통해 + Redis에서 결과 조회 + end note end deactivate Handler diff --git a/design/backend/sequence/inner/event-이미지결과조회.puml b/design/backend/sequence/inner/event-이미지결과조회.puml index 2f996fc..1a47290 100644 --- a/design/backend/sequence/inner/event-이미지결과조회.puml +++ b/design/backend/sequence/inner/event-이미지결과조회.puml @@ -4,50 +4,42 @@ title Event Service - 이미지 생성 결과 폴링 조회 participant "EventController" as Controller <> -participant "EventService" as EventSvc <> -participant "EventRepository" as EventRepo <> -participant "Event DB" as EventDB <> +participant "JobService" as JobSvc <> +participant "Redis Cache" as Cache <> -note over Controller: GET /api/events/{eventId}/status -Controller -> EventSvc: 이미지 생성 결과 조회 요청\n(이벤트ID) -activate EventSvc +note over Controller: GET /api/jobs/{jobId}/images +Controller -> JobSvc: getImageJobStatus(jobId) +activate JobSvc -EventSvc -> EventRepo: 이벤트 조회\n(이벤트ID) -activate EventRepo +JobSvc -> Cache: get("job:" + jobId) +activate Cache -EventRepo -> EventDB: 이벤트 정보 조회 -activate EventDB +alt 캐시 히트 + Cache --> JobSvc: Job data\n{status, imageUrls, createdAt} -alt 이벤트 존재 - EventDB --> EventRepo: 이벤트 엔티티\n{이벤트ID, 상태, 이미지URL들,\n생성일시} - deactivate EventDB - EventRepo --> EventSvc: Event 엔티티 + alt Job 완료 (status: COMPLETED) + JobSvc --> Controller: JobStatusResponse\n{jobId, status: COMPLETED,\nimageUrls: {...}} + Controller --> Client: 200 OK\n{status: COMPLETED,\nimageUrls: {\n simple: "https://cdn.../simple.png",\n fancy: "https://cdn.../fancy.png",\n trendy: "https://cdn.../trendy.png"\n}} - alt 이미지 생성 완료 (상태: COMPLETED) - EventSvc --> Controller: 완료 응답\n{이벤트ID, 상태: 완료,\n이미지URL들} - Controller --> Client: 200 OK\n{상태: 완료,\n이미지URL들: {\n 심플형: "https://cdn.../simple.png",\n 화려형: "https://cdn.../fancy.png",\n 트렌디형: "https://cdn.../trendy.png"\n}} - - else 이미지 생성중 (상태: PROCESSING) - EventSvc --> Controller: 진행중 응답\n{이벤트ID, 상태: 처리중,\n진행률: 33%} - Controller --> Client: 200 OK\n{상태: 처리중,\n진행률: 33%} + else Job 진행중 (status: PROCESSING) + JobSvc --> Controller: JobStatusResponse\n{jobId, status: PROCESSING} + Controller --> Client: 200 OK\n{status: PROCESSING} note right: 클라이언트는 3초 후\n재요청 - else 이미지 생성 실패 (상태: FAILED) - EventSvc --> Controller: 실패 응답\n{이벤트ID, 상태: 실패,\n오류메시지} - Controller --> Client: 200 OK\n{상태: 실패,\n오류메시지} + else Job 실패 (status: FAILED) + JobSvc --> Controller: JobStatusResponse\n{jobId, status: FAILED, error} + Controller --> Client: 200 OK\n{status: FAILED, error} end -else 이벤트 미존재 - EventDB --> EventRepo: null - deactivate EventDB - EventRepo --> EventSvc: null - EventSvc --> Controller: 미발견 오류 - Controller --> Client: 404 Not Found\n{오류: "이벤트를 찾을 수 없음"} +else 캐시 미스 + Cache --> JobSvc: null + JobSvc --> Controller: NotFoundError + Controller --> Client: 404 Not Found\n{error: "Job not found"} end -deactivate EventRepo -deactivate EventSvc +deactivate Cache +deactivate JobSvc -note over Controller, EventDB: 최대 30초 동안 폴링\n(3초 간격, 최대 10회)\n\n타임아웃 시 클라이언트는\n에러 메시지 표시 및\n"다시 생성" 옵션 제공 +note over Controller, Cache: 최대 30초 동안 폴링\n(3초 간격, 최대 10회)\n\n이미지 URL은 Redis에서 조회\n(TTL: 7일)\n\n타임아웃 시 클라이언트는\n에러 메시지 표시 및\n"다시 생성" 옵션 제공 @enduml diff --git a/design/backend/sequence/inner/event-콘텐츠생성완료구독.puml b/design/backend/sequence/inner/event-콘텐츠생성완료구독.puml deleted file mode 100644 index ac6835d..0000000 --- a/design/backend/sequence/inner/event-콘텐츠생성완료구독.puml +++ /dev/null @@ -1,70 +0,0 @@ -@startuml event-콘텐츠생성완료구독 -!theme mono - -title Event Service - 콘텐츠 생성 완료 이벤트 구독 (Kafka Consumer) - -participant "Kafka\nevent-topic\nConsumer" as Consumer -participant "EventHandler" as Handler -participant "EventRepository" as Repo -database "Event DB" as DB -participant "Redis Cache" as Cache - -note over Consumer: Kafka 구독\nevent-topic\n이벤트 타입: ContentCreated - -== ContentCreated 이벤트 수신 == -Consumer -> Handler: ContentCreated 이벤트 수신\n{jobId, eventDraftId, imageUrls} -activate Handler - -Handler -> Handler: 이벤트 검증\n- eventDraftId 유효성\n- imageUrls 존재 여부 -note right: 3가지 스타일 확인\n(simple, fancy, trendy) - -== Event DB에 이미지 URL 저장 == -Handler -> Repo: updateContentImages(eventDraftId, imageUrls) -activate Repo - -Repo -> DB: 이벤트 초안 업데이트\nUPDATE event_drafts\nSET content_images = ?,\n updated_at = NOW()\nWHERE event_draft_id = ? -activate DB - -note over DB - 저장 데이터: - { - "simple": "https://cdn.../simple.png", - "fancy": "https://cdn.../fancy.png", - "trendy": "https://cdn.../trendy.png" - } -end note - -DB --> Repo: 업데이트 완료 -deactivate DB - -Repo --> Handler: 저장 완료 -deactivate Repo - -== 캐시 무효화 == -Handler -> Cache: 이벤트 초안 캐시 무효화\nDEL event:draft:{eventDraftId} -activate Cache -Cache --> Handler: 삭제 완료 -deactivate Cache - -note over Handler: 캐시 무효화로\n다음 조회 시 최신 데이터 반영 - -Handler --> Consumer: 처리 완료 -deactivate Handler - -note over Consumer, DB -**처리 전략** -- At-Least-Once 전달 보장 -- 멱등성 처리 (중복 이벤트 무시) -- 실패 시 자동 재시도 (최대 3회) -- Dead Letter Queue로 실패 이벤트 이동 - -**저장 위치** -- Event DB: 영구 저장 (이벤트 초안 테이블) -- Redis Cache: 임시 저장 (7일 TTL) - -**데이터 정합성** -- DB 저장 후 캐시 무효화 -- 다음 조회 시 DB에서 최신 데이터 로드 -end note - -@enduml