From 9ee2178b57d570a3d73be3ec086a59d86de5e41c Mon Sep 17 00:00:00 2001 From: Hyowon Yang Date: Thu, 23 Oct 2025 13:17:57 +0900 Subject: [PATCH] =?UTF-8?q?redis=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../sequence/inner/content-์ด๋ฏธ์ง€์ƒ์„ฑ.puml | 18 ++--- .../sequence/inner/event-์ด๋ฏธ์ง€๊ฒฐ๊ณผ์กฐํšŒ.puml | 58 +++++++-------- .../inner/event-์ฝ˜ํ…์ธ ์ƒ์„ฑ์™„๋ฃŒ๊ตฌ๋….puml | 70 ------------------- 3 files changed, 30 insertions(+), 116 deletions(-) delete mode 100644 design/backend/sequence/inner/event-์ฝ˜ํ…์ธ ์ƒ์„ฑ์™„๋ฃŒ๊ตฌ๋….puml 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