openapi: 3.0.3 info: title: Content Service API version: 1.0.0 description: | # KT AI 기반 소상공인 이벤트 자동 생성 서비스 - Content Service API ## 주요 기능 - **SNS 이미지 생성** (UFR-CONT-010): AI 기반 이벤트 이미지 자동 생성 - **콘텐츠 편집** (UFR-CONT-020): 생성된 이미지 조회, 재생성, 삭제 - **3가지 스타일**: 심플(SIMPLE), 화려한(FANCY), 트렌디(TRENDY) - **3개 플랫폼 최적화**: Instagram (1080x1080), Naver (800x600), Kakao (800x800) - **Redis 캐싱**: TTL 7일, 동일 eventDraftId 재요청 시 캐시 반환 - **CDN 이미지 저장**: Azure Blob Storage 기반 ## 비동기 처리 아키텍처 ### Kafka Job Consumer **Topic**: `image-generation-job` **처리 흐름**: 1. Kafka에서 이미지 생성 Job 수신 (EventService에서 발행) 2. Redis에서 AI Service 이벤트 데이터 조회 3. Redis 캐시에서 기존 이미지 확인 (동일 eventDraftId) 4. 외부 이미지 생성 API 호출 (Stable Diffusion / DALL-E) - **Circuit Breaker**: 5분 타임아웃, 실패율 50% 초과 시 Open - **Fallback**: Stable Diffusion → DALL-E → 기본 템플릿 이미지 5. 생성된 이미지 CDN(Azure Blob) 업로드 6. Redis에 이미지 URL 저장 (TTL 7일) 7. Job 상태 업데이트 (PENDING → PROCESSING → COMPLETED/FAILED) **Job Payload Schema**: `ImageGenerationJob` (components/schemas 참조) ## 외부 API 연동 - **Image Generation API**: Stable Diffusion / DALL-E - **Circuit Breaker**: 5분 타임아웃, 50% 실패율 임계값 - **CDN**: Azure Blob Storage (이미지 업로드) ## 출력 형식 - 스타일별 3개 × 플랫폼별 3개 = **총 9개 이미지** 생성 (현재는 Instagram만) - CDN URL 반환 contact: name: Content Service Team email: content-team@kt.com servers: - url: http://localhost:8083 description: Content Service Local Development Server - url: https://api-dev.kt-event.com description: Development Server - url: https://api.kt-event.com description: Production Server tags: - name: Job Status description: 이미지 생성 작업 상태 조회 (비동기 폴링) - name: Content Management description: 생성된 콘텐츠 조회 및 관리 (UFR-CONT-020) - name: Image Management description: 이미지 재생성 및 삭제 (UFR-CONT-020) paths: /api/content/images/generate: post: tags: - Job Status summary: SNS 이미지 생성 요청 (비동기) description: | 이벤트 정보를 기반으로 3가지 스타일의 SNS 이미지 생성을 비동기로 요청합니다. ## 처리 방식 - **비동기 처리**: Kafka `image-generation-job` 토픽에 Job 발행 - **폴링 조회**: jobId로 생성 상태 조회 (GET /api/content/images/jobs/{jobId}) - **캐싱**: 동일한 eventDraftId 재요청 시 캐시 반환 (TTL 7일) ## 생성 스타일 1. **심플 스타일 (SIMPLE)**: 깔끔한 디자인, 텍스트 중심 2. **화려한 스타일 (FANCY)**: 눈에 띄는 디자인, 풍부한 색상 3. **트렌디 스타일 (TRENDY)**: 최신 트렌드, MZ세대 타겟 ## Resilience 패턴 - Circuit Breaker (5분 타임아웃, 실패율 50% 초과 시 Open) - Fallback (Stable Diffusion → DALL-E → 기본 템플릿) operationId: generateImages x-user-story: UFR-CONT-010 x-controller: ImageGenerationController.generateImages requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ImageGenerationRequest' examples: basicEvent: summary: 기본 이벤트 value: eventDraftId: "evt-draft-12345" eventInfo: title: "봄맞이 커피 할인 이벤트" giftName: "아메리카노 1+1" brandColor: "#FF5733" logoUrl: "https://cdn.example.com/logo.png" withoutLogo: summary: 로고 없는 이벤트 value: eventDraftId: "evt-draft-67890" eventInfo: title: "신메뉴 출시 기념 경품 추첨" giftName: "스타벅스 기프티콘 5000원권" brandColor: "#00704A" responses: '202': description: | 이미지 생성 요청이 성공적으로 접수되었습니다. jobId를 사용하여 생성 상태를 폴링 조회하세요. content: application/json: schema: $ref: '#/components/schemas/ImageGenerationAcceptedResponse' examples: accepted: summary: 요청 접수 성공 value: jobId: "job-img-abc123" eventDraftId: "evt-draft-12345" status: "PENDING" estimatedCompletionTime: 5 message: "이미지 생성 요청이 접수되었습니다. jobId로 결과를 조회하세요." '400': description: 잘못된 요청 (필수 필드 누락, 유효하지 않은 데이터) content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' examples: missingField: summary: 필수 필드 누락 value: code: "BAD_REQUEST" message: "eventInfo.title은 필수 항목입니다." timestamp: "2025-10-22T14:30:00Z" invalidColor: summary: 유효하지 않은 색상 코드 value: code: "BAD_REQUEST" message: "brandColor는 HEX 색상 코드 형식이어야 합니다." timestamp: "2025-10-22T14:30:00Z" '429': description: 요청 제한 초과 (Rate Limiting) content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' examples: rateLimitExceeded: summary: 요청 제한 초과 value: code: "RATE_LIMIT_EXCEEDED" message: "요청 한도를 초과했습니다. 1분 후 다시 시도하세요." timestamp: "2025-10-22T14:30:00Z" retryAfter: 60 '500': description: 서버 내부 오류 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' examples: internalError: summary: 서버 내부 오류 value: code: "INTERNAL_SERVER_ERROR" message: "이미지 생성 요청 처리 중 오류가 발생했습니다." timestamp: "2025-10-22T14:30:00Z" security: - BearerAuth: [] /api/content/images/jobs/{jobId}: get: tags: - Job Status summary: 이미지 생성 작업 상태 조회 (폴링) description: | jobId로 이미지 생성 상태를 조회합니다. ## 폴링 권장사항 - **폴링 간격**: 2초 - **최대 폴링 시간**: 30초 - **Timeout 후 처리**: 에러 메시지 표시 및 재시도 옵션 제공 ## 상태 종류 - **PENDING**: 대기 중 (Kafka Queue에서 대기) - **PROCESSING**: 생성 중 (AI API 호출 진행) - **COMPLETED**: 완료 (3가지 이미지 URL 반환) - **FAILED**: 실패 (에러 메시지 포함, Fallback 이미지 제공) ## 캐싱 - COMPLETED 상태는 Redis 캐싱 (TTL 7일) - 동일한 eventDraftId 재요청 시 즉시 반환 operationId: getImageGenerationStatus x-user-story: UFR-CONT-010 x-controller: ImageGenerationController.getJobStatus parameters: - name: jobId in: path required: true description: 이미지 생성 Job ID schema: type: string example: "job-img-abc123" responses: '200': description: 이미지 생성 상태 조회 성공 content: application/json: schema: $ref: '#/components/schemas/ImageGenerationStatusResponse' examples: pending: summary: 대기 중 value: jobId: "job-img-abc123" status: "PENDING" message: "이미지 생성 대기 중입니다." estimatedCompletionTime: 5 processing: summary: 생성 중 value: jobId: "job-img-abc123" status: "PROCESSING" message: "AI가 이벤트에 어울리는 이미지를 생성하고 있어요..." estimatedCompletionTime: 3 completed: summary: 생성 완료 value: jobId: "job-img-abc123" status: "COMPLETED" message: "이미지 생성이 완료되었습니다." images: - style: "SIMPLE" url: "https://cdn.kt-event.com/images/evt-draft-12345-simple.png" platform: "INSTAGRAM" size: width: 1080 height: 1080 - style: "FANCY" url: "https://cdn.kt-event.com/images/evt-draft-12345-fancy.png" platform: "INSTAGRAM" size: width: 1080 height: 1080 - style: "TRENDY" url: "https://cdn.kt-event.com/images/evt-draft-12345-trendy.png" platform: "INSTAGRAM" size: width: 1080 height: 1080 completedAt: "2025-10-22T14:30:05Z" fromCache: false completedFromCache: summary: 캐시에서 반환 value: jobId: "job-img-def456" status: "COMPLETED" message: "이미지 생성이 완료되었습니다. (캐시)" images: - style: "SIMPLE" url: "https://cdn.kt-event.com/images/evt-draft-12345-simple.png" platform: "INSTAGRAM" size: width: 1080 height: 1080 - style: "FANCY" url: "https://cdn.kt-event.com/images/evt-draft-12345-fancy.png" platform: "INSTAGRAM" size: width: 1080 height: 1080 - style: "TRENDY" url: "https://cdn.kt-event.com/images/evt-draft-12345-trendy.png" platform: "INSTAGRAM" size: width: 1080 height: 1080 completedAt: "2025-10-22T14:28:00Z" fromCache: true failed: summary: 생성 실패 value: jobId: "job-img-abc123" status: "FAILED" message: "이미지 생성에 실패했습니다." error: code: "IMAGE_GENERATION_FAILED" detail: "외부 AI API 응답 시간 초과. 기본 템플릿으로 대체되었습니다." completedAt: "2025-10-22T14:30:25Z" '404': description: Job ID를 찾을 수 없음 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' examples: notFound: summary: Job ID 없음 value: code: "NOT_FOUND" message: "Job ID를 찾을 수 없습니다." timestamp: "2025-10-22T14:30:00Z" '500': description: 서버 내부 오류 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' examples: internalError: summary: 서버 내부 오류 value: code: "INTERNAL_SERVER_ERROR" message: "상태 조회 중 오류가 발생했습니다." timestamp: "2025-10-22T14:30:00Z" security: - BearerAuth: [] /api/content/events/{eventDraftId}: get: tags: - Content Management summary: 이벤트의 생성된 콘텐츠 조회 description: | 특정 이벤트의 생성된 모든 콘텐츠(이미지) 조회 - Redis 캐시에서 조회 - TTL 7일 이내 데이터만 조회 가능 - 캐시 만료 시 404 반환 operationId: getContentByEventId x-user-story: UFR-CONT-020 x-controller: ContentController.getContentByEventId parameters: - name: eventDraftId in: path required: true description: 이벤트 초안 ID schema: type: string example: "evt-draft-12345" responses: '200': description: 콘텐츠 조회 성공 content: application/json: schema: $ref: '#/components/schemas/ContentResponse' examples: success: summary: 콘텐츠 조회 성공 value: eventDraftId: "evt-draft-12345" images: - imageId: "img-12345-simple" style: "SIMPLE" url: "https://cdn.kt-event.com/images/evt-draft-12345-simple.png" platform: "INSTAGRAM" size: width: 1080 height: 1080 createdAt: "2025-10-22T14:30:05Z" - imageId: "img-12345-fancy" style: "FANCY" url: "https://cdn.kt-event.com/images/evt-draft-12345-fancy.png" platform: "INSTAGRAM" size: width: 1080 height: 1080 createdAt: "2025-10-22T14:30:10Z" - imageId: "img-12345-trendy" style: "TRENDY" url: "https://cdn.kt-event.com/images/evt-draft-12345-trendy.png" platform: "INSTAGRAM" size: width: 1080 height: 1080 createdAt: "2025-10-22T14:30:15Z" totalCount: 3 createdAt: "2025-10-22T14:30:00Z" expiresAt: "2025-10-29T14:30:00Z" '404': description: 콘텐츠를 찾을 수 없음 (생성 중이거나 만료됨) content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' examples: notFound: summary: 콘텐츠 없음 value: code: "CONTENT_NOT_FOUND" message: "해당 이벤트의 콘텐츠를 찾을 수 없습니다." timestamp: "2025-10-22T14:30:00Z" '500': description: 서버 내부 오류 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' security: - BearerAuth: [] /api/content/events/{eventDraftId}/images: get: tags: - Content Management summary: 이벤트의 이미지 목록 조회 (필터링) description: | 특정 이벤트의 모든 생성 이미지 목록 조회 - 스타일별, 플랫폼별 필터링 지원 operationId: getImages x-user-story: UFR-CONT-020 x-controller: ContentController.getImages parameters: - name: eventDraftId in: path required: true description: 이벤트 초안 ID schema: type: string example: "evt-draft-12345" - name: style in: query required: false description: 이미지 스타일 필터 schema: type: string enum: [SIMPLE, FANCY, TRENDY] example: "SIMPLE" - name: platform in: query required: false description: 플랫폼 필터 schema: type: string enum: [INSTAGRAM, NAVER_BLOG, KAKAO_CHANNEL] example: "INSTAGRAM" responses: '200': description: 이미지 목록 조회 성공 content: application/json: schema: type: object properties: eventDraftId: type: string totalCount: type: integer images: type: array items: $ref: '#/components/schemas/GeneratedImage' examples: allImages: summary: 전체 이미지 조회 value: eventDraftId: "evt-draft-12345" totalCount: 3 images: - imageId: "img-12345-simple" style: "SIMPLE" url: "https://cdn.kt-event.com/images/evt-draft-12345-simple.png" platform: "INSTAGRAM" size: width: 1080 height: 1080 createdAt: "2025-10-22T14:30:05Z" '404': description: 이미지를 찾을 수 없음 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' security: - BearerAuth: [] /api/content/images/{imageId}: get: tags: - Image Management summary: 특정 이미지 상세 조회 description: 이미지 ID로 특정 이미지의 상세 정보 조회 operationId: getImageById x-user-story: UFR-CONT-020 x-controller: ContentController.getImageById parameters: - name: imageId in: path required: true description: 이미지 ID schema: type: string example: "img-12345-simple" responses: '200': description: 이미지 조회 성공 content: application/json: schema: $ref: '#/components/schemas/GeneratedImage' examples: success: summary: 이미지 조회 성공 value: imageId: "img-12345-simple" style: "SIMPLE" url: "https://cdn.kt-event.com/images/evt-draft-12345-simple.png" platform: "INSTAGRAM" size: width: 1080 height: 1080 createdAt: "2025-10-22T14:30:05Z" '404': description: 이미지를 찾을 수 없음 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' security: - BearerAuth: [] delete: tags: - Image Management summary: 생성된 이미지 삭제 description: | 특정 이미지 삭제 - Redis 캐시에서 제거 - CDN 이미지는 유지 (비용 고려) operationId: deleteImage x-user-story: UFR-CONT-020 x-controller: ContentController.deleteImage parameters: - name: imageId in: path required: true description: 이미지 ID schema: type: string example: "img-12345-simple" responses: '204': description: 이미지 삭제 성공 '404': description: 이미지를 찾을 수 없음 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' security: - BearerAuth: [] /api/content/images/{imageId}/regenerate: post: tags: - Image Management summary: 이미지 재생성 요청 description: | 특정 이미지를 재생성 (콘텐츠 편집) - 동일한 스타일/플랫폼으로 재생성 - 프롬프트 수정 가능 - 비동기 처리 (Kafka Job 발행) operationId: regenerateImage x-user-story: UFR-CONT-020 x-controller: ContentController.regenerateImage parameters: - name: imageId in: path required: true description: 이미지 ID schema: type: string example: "img-12345-simple" requestBody: required: false description: 재생성 옵션 (선택사항) content: application/json: schema: $ref: '#/components/schemas/ImageRegenerationRequest' examples: modifyPrompt: summary: 프롬프트 수정 value: content: "밝은 분위기로 변경" changeStyle: summary: 스타일 변경 value: style: "FANCY" responses: '202': description: 재생성 요청 접수 (비동기 처리) content: application/json: schema: type: object required: - message - jobId - estimatedTime properties: message: type: string example: "이미지 재생성 요청이 접수되었습니다" jobId: type: string example: "job-regen-abc123" estimatedTime: type: integer description: 예상 소요 시간 (초) example: 10 '404': description: 원본 이미지를 찾을 수 없음 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '503': description: 이미지 생성 서비스 장애 (Circuit Breaker Open) content: application/json: schema: $ref: '#/components/schemas/CircuitBreakerErrorResponse' security: - BearerAuth: [] components: securitySchemes: BearerAuth: type: http scheme: bearer bearerFormat: JWT description: JWT 토큰을 Authorization 헤더에 포함 (Bearer {token}) schemas: # ======================================== # Kafka Job Schema (비동기 처리) # ======================================== ImageGenerationJob: type: object description: | **Kafka Topic**: `image-generation-job` Event Service에서 발행하여 Content Service가 소비하는 Job Payload - Content Service의 Kafka Consumer가 처리 - 비동기 이미지 생성 작업 수행 required: - jobId - eventDraftId - eventInfo properties: jobId: type: string description: Job ID (작업 추적용) example: "job-img-abc123" eventDraftId: type: string description: 이벤트 초안 ID example: "evt-draft-12345" eventInfo: type: object description: 이벤트 정보 (AI Service에서 생성) required: - title - giftName properties: title: type: string description: 이벤트 제목 example: "봄맞이 커피 할인 이벤트" giftName: type: string description: 경품명 example: "아메리카노 1+1" brandColor: type: string description: 브랜드 컬러 (HEX) pattern: '^#[0-9A-Fa-f]{6}$' example: "#FF5733" logoUrl: type: string format: uri description: 로고 이미지 URL (선택) example: "https://cdn.example.com/logo.png" styles: type: array description: 생성할 스타일 목록 (기본값 전체) items: type: string enum: [SIMPLE, FANCY, TRENDY] example: ["SIMPLE", "FANCY", "TRENDY"] platforms: type: array description: 생성할 플랫폼 목록 (기본값 Instagram) items: type: string enum: [INSTAGRAM, NAVER_BLOG, KAKAO_CHANNEL] example: ["INSTAGRAM"] priority: type: integer description: 우선순위 (1-10, 높을수록 우선) minimum: 1 maximum: 10 example: 5 requestedAt: type: string format: date-time description: 요청 시각 example: "2025-10-22T14:00:00Z" # ======================================== # Request Schemas # ======================================== ImageGenerationRequest: type: object required: - eventDraftId - eventInfo properties: eventDraftId: type: string description: | 이벤트 초안 ID (Event Service에서 발급) 동일한 eventDraftId로 재요청 시 캐시된 이미지 반환 example: "evt-draft-12345" eventInfo: type: object required: - title - giftName properties: title: type: string description: 이벤트 제목 (최대 50자) minLength: 1 maxLength: 50 example: "봄맞이 커피 할인 이벤트" giftName: type: string description: 경품명 (최대 30자) minLength: 1 maxLength: 30 example: "아메리카노 1+1" brandColor: type: string description: | 브랜드 컬러 (HEX 색상 코드) 사용자 프로필에서 가져오거나 기본값 사용 pattern: '^#[0-9A-Fa-f]{6}$' example: "#FF5733" logoUrl: type: string format: uri description: | 로고 이미지 URL (선택) 업로드된 경우에만 제공 example: "https://cdn.example.com/logo.png" ImageGenerationAcceptedResponse: type: object required: - jobId - eventDraftId - status - message properties: jobId: type: string description: 이미지 생성 Job ID (폴링 조회에 사용) example: "job-img-abc123" eventDraftId: type: string description: 이벤트 초안 ID example: "evt-draft-12345" status: type: string enum: [PENDING] description: 초기 상태는 항상 PENDING example: "PENDING" estimatedCompletionTime: type: integer description: 예상 완료 시간 (초) example: 5 message: type: string description: 응답 메시지 example: "이미지 생성 요청이 접수되었습니다. jobId로 결과를 조회하세요." ImageGenerationStatusResponse: type: object required: - jobId - status - message properties: jobId: type: string description: 이미지 생성 Job ID example: "job-img-abc123" status: type: string enum: [PENDING, PROCESSING, COMPLETED, FAILED] description: | Job 상태 - PENDING: 대기 중 - PROCESSING: 생성 중 - COMPLETED: 완료 - FAILED: 실패 example: "COMPLETED" message: type: string description: 상태 메시지 example: "이미지 생성이 완료되었습니다." estimatedCompletionTime: type: integer description: 예상 완료 시간 (초, PENDING/PROCESSING 상태에서만) example: 3 images: type: array description: 생성된 이미지 배열 (COMPLETED 상태에서만) items: $ref: '#/components/schemas/GeneratedImage' completedAt: type: string format: date-time description: 완료 시각 (COMPLETED/FAILED 상태에서만) example: "2025-10-22T14:30:05Z" fromCache: type: boolean description: 캐시에서 반환 여부 (COMPLETED 상태에서만) example: false error: $ref: '#/components/schemas/JobError' GeneratedImage: type: object required: - style - url - platform - size properties: style: type: string enum: [SIMPLE, FANCY, TRENDY] description: | 이미지 스타일 - SIMPLE: 심플 스타일 (깔끔한 디자인, 텍스트 중심) - FANCY: 화려한 스타일 (눈에 띄는 디자인, 풍부한 색상) - TRENDY: 트렌디 스타일 (최신 트렌드, MZ세대 타겟) example: "SIMPLE" url: type: string format: uri description: CDN 이미지 URL example: "https://cdn.kt-event.com/images/evt-draft-12345-simple.png" platform: type: string enum: [INSTAGRAM, NAVER_BLOG, KAKAO_CHANNEL] description: | 플랫폼별 최적화 - INSTAGRAM: 1080x1080 - NAVER_BLOG: 800x600 - KAKAO_CHANNEL: 800x800 example: "INSTAGRAM" size: type: object required: - width - height properties: width: type: integer description: 이미지 너비 (픽셀) example: 1080 height: type: integer description: 이미지 높이 (픽셀) example: 1080 JobError: type: object required: - code - detail description: Job 실패 시 에러 정보 (FAILED 상태에서만) properties: code: type: string description: 에러 코드 example: "IMAGE_GENERATION_FAILED" detail: type: string description: 상세 에러 메시지 example: "외부 AI API 응답 시간 초과. 기본 템플릿으로 대체되었습니다." ErrorResponse: type: object required: - code - message - timestamp properties: code: type: string description: 에러 코드 example: "BAD_REQUEST" message: type: string description: 에러 메시지 example: "eventInfo.title은 필수 항목입니다." timestamp: type: string format: date-time description: 에러 발생 시각 example: "2025-10-22T14:30:00Z" retryAfter: type: integer description: 재시도 대기 시간 (초, Rate Limiting 에러에서만) example: 60 # ======================================== # Content Management Schemas (UFR-CONT-020) # ======================================== ContentResponse: type: object description: 이벤트의 생성된 콘텐츠 전체 정보 required: - eventDraftId - images - totalCount - createdAt - expiresAt properties: eventDraftId: type: string description: 이벤트 초안 ID example: "evt-draft-12345" images: type: array description: 생성된 이미지 목록 items: $ref: '#/components/schemas/ImageDetail' totalCount: type: integer description: 총 이미지 개수 example: 3 createdAt: type: string format: date-time description: 콘텐츠 생성 시각 example: "2025-10-22T14:30:00Z" expiresAt: type: string format: date-time description: 캐시 만료 시각 (TTL 7일) example: "2025-10-29T14:30:00Z" ImageDetail: type: object description: 상세 이미지 정보 (생성 시각 포함) required: - imageId - style - url - platform - size - createdAt properties: imageId: type: string description: 이미지 ID example: "img-12345-simple" style: type: string enum: [SIMPLE, FANCY, TRENDY] description: 이미지 스타일 example: "SIMPLE" url: type: string format: uri description: CDN 이미지 URL (Azure Blob Storage) example: "https://cdn.kt-event.com/images/evt-draft-12345-simple.png" platform: type: string enum: [INSTAGRAM, NAVER_BLOG, KAKAO_CHANNEL] description: 플랫폼 example: "INSTAGRAM" size: type: object required: - width - height properties: width: type: integer description: 이미지 너비 (픽셀) example: 1080 height: type: integer description: 이미지 높이 (픽셀) example: 1080 createdAt: type: string format: date-time description: 이미지 생성 시각 example: "2025-10-22T14:30:05Z" fallbackUsed: type: boolean description: Fallback 이미지 사용 여부 example: false ImageRegenerationRequest: type: object description: 이미지 재생성 요청 (콘텐츠 편집) properties: content: type: string description: 수정된 프롬프트 (선택사항) example: "밝은 분위기로 변경" style: type: string description: 변경할 스타일 (선택사항) enum: [SIMPLE, FANCY, TRENDY] example: "FANCY" CircuitBreakerErrorResponse: type: object description: Circuit Breaker 오류 응답 (외부 API 장애) required: - code - message - timestamp - circuitBreakerState properties: code: type: string description: 에러 코드 example: "IMAGE_GENERATION_SERVICE_UNAVAILABLE" message: type: string description: 에러 메시지 example: "이미지 생성 서비스가 일시적으로 사용 불가능합니다" timestamp: type: string format: date-time description: 에러 발생 시각 example: "2025-10-22T14:30:00Z" circuitBreakerState: type: string enum: [OPEN, HALF_OPEN, CLOSED] description: Circuit Breaker 상태 example: "OPEN" fallbackAvailable: type: boolean description: Fallback 이미지 사용 가능 여부 example: true fallbackImageUrl: type: string format: uri description: Fallback 템플릿 이미지 URL (사용 가능한 경우) example: "https://cdn.kt-event.com/templates/default_event.png" retryAfter: type: integer description: 재시도 가능 시간 (초) example: 300