@startuml content-이미지생성 !theme mono title Content Service - 이미지 생성 내부 시퀀스 (UFR-CONT-010) actor Client participant "Kafka\nimage-job\nConsumer" as Consumer participant "JobHandler" as Handler participant "CacheManager" as Cache participant "ImageGenerator" as Generator participant "ImageStyleFactory" as Factory participant "StableDiffusion\nAPI Client" as SDClient participant "DALL-E\nAPI Client" as DALLEClient participant "Circuit Breaker" as CB participant "BlobStorage\nUploader" as BlobStorage participant "JobStatusManager" as JobStatus database "Redis Cache" as Redis note over Consumer: Kafka 구독\nimage-job 토픽 == Kafka Job 수신 == Consumer -> Handler: Job Message 수신\n{jobId, eventDraftId, eventInfo} activate Handler Handler -> Cache: 캐시 조회\nkey: content:image:{eventDraftId} activate Cache Cache -> Redis: GET content:image:{eventDraftId} Redis --> Cache: 캐시 데이터 또는 NULL Cache --> Handler: 캐시 결과 deactivate Cache alt 캐시 HIT (기존 이미지 존재) Handler -> JobStatus: Job 상태 업데이트\nstatus: COMPLETED (캐시) activate JobStatus JobStatus -> Redis: SET job:{jobId}\n{status: COMPLETED, imageUrls: [...]} JobStatus --> Handler: 업데이트 완료 deactivate JobStatus Handler --> Consumer: 처리 완료 (캐시) else 캐시 MISS (새로운 이미지 생성) Handler -> JobStatus: Job 상태 업데이트\nstatus: PROCESSING activate JobStatus JobStatus -> Redis: SET job:{jobId}\n{status: PROCESSING} JobStatus --> Handler: 업데이트 완료 deactivate JobStatus Handler -> Generator: 3가지 스타일 이미지 생성 요청\n{eventInfo} activate Generator == 3가지 스타일 병렬 생성 (par 블록) == group parallel Generator -> Factory: 심플 프롬프트 생성\n{eventInfo, style: SIMPLE} activate Factory Factory --> Generator: 심플 프롬프트 deactivate Factory Generator -> CB: Circuit Breaker 체크 activate CB CB --> Generator: State: CLOSED (정상) deactivate CB Generator -> SDClient: Stable Diffusion API 호출\n{prompt, style: SIMPLE}\nTimeout: 20초 activate SDClient note over SDClient: Circuit Breaker 적용\nRetry: 최대 3회\nTimeout: 20초 alt API 성공 SDClient --> Generator: 심플 이미지 데이터 deactivate SDClient Generator -> BlobStorage: Blob 업로드 요청\n{imageData, eventId, style: SIMPLE}\nRetry: 3회, Timeout: 30초 activate BlobStorage note right of BlobStorage: SAS Token 생성\n(유효기간 7일) BlobStorage --> Generator: Blob SAS URL (심플) deactivate BlobStorage else API 실패 (Timeout/Error) SDClient --> Generator: 실패 응답 deactivate SDClient Generator -> CB: 실패 기록 activate CB CB -> CB: 실패율 계산 alt 실패율 > 50% CB -> CB: Circuit State: OPEN end CB --> Generator: Circuit State deactivate CB Generator -> DALLEClient: Fallback - DALL-E API 호출\n{prompt, style: SIMPLE}\nTimeout: 20초 activate DALLEClient alt Fallback 성공 DALLEClient --> Generator: 심플 이미지 데이터 deactivate DALLEClient Generator -> BlobStorage: Blob 업로드 요청\n{imageData, eventId, style: SIMPLE}\nRetry: 3회, Timeout: 30초 activate BlobStorage note right of BlobStorage: SAS Token 생성\n(유효기간 7일) BlobStorage --> Generator: Blob SAS URL (심플) deactivate BlobStorage else Fallback 실패 DALLEClient --> Generator: 실패 응답 deactivate DALLEClient Generator -> Generator: 기본 템플릿 사용\n(심플) end end Generator -> Factory: 화려한 프롬프트 생성\n{eventInfo, style: FANCY} activate Factory Factory --> Generator: 화려한 프롬프트 deactivate Factory Generator -> CB: Circuit Breaker 체크 activate CB CB --> Generator: State: CLOSED/OPEN deactivate CB alt Circuit CLOSED Generator -> SDClient: Stable Diffusion API 호출\n{prompt, style: FANCY}\nTimeout: 20초 activate SDClient alt API 성공 SDClient --> Generator: 화려한 이미지 데이터 deactivate SDClient Generator -> BlobStorage: Blob 업로드 요청\n{imageData, eventId, style: FANCY}\nRetry: 3회, Timeout: 30초 activate BlobStorage note right of BlobStorage: SAS Token 생성\n(유효기간 7일) BlobStorage --> Generator: Blob SAS URL (화려한) deactivate BlobStorage else API 실패 SDClient --> Generator: 실패 응답 deactivate SDClient Generator -> DALLEClient: Fallback - DALL-E API 호출 activate DALLEClient alt Fallback 성공 DALLEClient --> Generator: 화려한 이미지 데이터 deactivate DALLEClient Generator -> BlobStorage: Blob 업로드\n{imageData, eventId, style: FANCY}\nRetry: 3회, Timeout: 30초 activate BlobStorage note right of BlobStorage: SAS Token 생성\n(유효기간 7일) BlobStorage --> Generator: Blob SAS URL (화려한) deactivate BlobStorage else Fallback 실패 DALLEClient --> Generator: 실패 응답 deactivate DALLEClient Generator -> Generator: 기본 템플릿 사용\n(화려한) end end else Circuit OPEN Generator -> Generator: Circuit Open\n즉시 기본 템플릿 사용 end Generator -> Factory: 트렌디 프롬프트 생성\n{eventInfo, style: TRENDY} activate Factory Factory --> Generator: 트렌디 프롬프트 deactivate Factory Generator -> CB: Circuit Breaker 체크 activate CB CB --> Generator: State: CLOSED/OPEN deactivate CB alt Circuit CLOSED Generator -> SDClient: Stable Diffusion API 호출\n{prompt, style: TRENDY}\nTimeout: 20초 activate SDClient alt API 성공 SDClient --> Generator: 트렌디 이미지 데이터 deactivate SDClient Generator -> BlobStorage: Blob 업로드 요청\n{imageData, eventId, style: TRENDY}\nRetry: 3회, Timeout: 30초 activate BlobStorage note right of BlobStorage: SAS Token 생성\n(유효기간 7일) BlobStorage --> Generator: Blob SAS URL (트렌디) deactivate BlobStorage else API 실패 SDClient --> Generator: 실패 응답 deactivate SDClient Generator -> DALLEClient: Fallback - DALL-E API 호출 activate DALLEClient alt Fallback 성공 DALLEClient --> Generator: 트렌디 이미지 데이터 deactivate DALLEClient Generator -> BlobStorage: Blob 업로드\n{imageData, eventId, style: TRENDY}\nRetry: 3회, Timeout: 30초 activate BlobStorage note right of BlobStorage: SAS Token 생성\n(유효기간 7일) BlobStorage --> Generator: Blob SAS URL (트렌디) deactivate BlobStorage else Fallback 실패 DALLEClient --> Generator: 실패 응답 deactivate DALLEClient Generator -> Generator: 기본 템플릿 사용\n(트렌디) end end else Circuit OPEN Generator -> Generator: Circuit Open\n즉시 기본 템플릿 사용 end end Generator --> Handler: 3가지 이미지 URL 반환\n{simple, fancy, trendy} deactivate Generator == 결과 캐싱 및 Job 완료 == Handler -> Cache: Blob SAS URL 캐싱\nkey: content:image:{eventDraftId}\nTTL: 7일 activate Cache Cache -> Redis: SET content:image:{eventDraftId}\n{simple: SAS_URL, fancy: SAS_URL, trendy: SAS_URL}\nTTL: 604800 (7일) Redis --> Cache: 저장 완료 Cache --> Handler: 캐싱 완료 deactivate Cache Handler -> JobStatus: Job 상태 업데이트\nstatus: COMPLETED activate JobStatus JobStatus -> Redis: SET job:{jobId}\n{status: COMPLETED, imageUrls: [...]} JobStatus --> Handler: 업데이트 완료 deactivate JobStatus Handler --> Consumer: 처리 완료 note over Handler Blob SAS URL은 Redis에만 저장됨 Event Service는 폴링을 통해 Redis에서 결과 조회 SAS Token 유효기간: 7일 end note end deactivate Handler note over Consumer, Redis **Resilience 패턴 적용** - Circuit Breaker: 실패율 50% 초과 시 Open (AI API용) - AI API Timeout: 20초 - Fallback: Stable Diffusion 실패 시 DALL-E, 모두 실패 시 기본 템플릿 - Blob Storage Retry: 최대 3회 (Exponential Backoff: 1s, 2s, 4s) - Blob Storage Timeout: 30초 (대용량 이미지 고려) - Cache-Aside: Redis 캐싱 (TTL 7일) **처리 시간** - 캐시 HIT: 0.1초 - 캐시 MISS: 5.2초 이내 (병렬 처리) └─ AI 생성: 3-5초 + Blob 업로드: 0.15-0.3초 **병렬 처리** - 3가지 스타일 동시 생성 (par 블록) - 독립적인 스레드 풀 사용 **Blob Storage 업로드** - Azure Blob Storage (Korea Central) - SAS Token 기반 접근 제어 (읽기 전용) - SAS Token 유효기간: 7일 (Redis TTL과 동기화) - Public Access 비활성화 (보안 강화) - Container: event-images - URL 형식: https://{account}.blob.core.windows.net/event-images/{id}-{style}.png?{sas_token} **보안** - Storage Account Public Access 비활성화 - SAS Token 기반 URL 생성 (읽기 전용 권한) - Firewall 규칙: K8s Cluster IP만 허용 - HTTPS 강제 (TLS 1.2 이상) end note @enduml