openapi: 3.0.3 info: title: Content Service API description: | KT AI 기반 소상공인 이벤트 자동 생성 서비스 - Content Service API ## 주요 기능 - SNS 이미지 생성 (UFR-CONT-010) - 3가지 스타일 이미지 자동 생성 (심플, 화려한, 트렌디) - AI 기반 이미지 생성 (Stable Diffusion / DALL-E) - Circuit Breaker 및 Fallback 패턴 적용 - Redis 캐싱 (TTL 7일) ## 비동기 처리 방식 - Kafka 기반 Job 처리 - 폴링 방식으로 결과 조회 - Event Service와 느슨한 결합 version: 1.0.0 contact: name: Content Service Team email: content-team@kt.com servers: - url: https://api.kt-event.com/v1 description: Production Server - url: https://api-dev.kt-event.com/v1 description: Development Server tags: - name: Images description: SNS 이미지 생성 및 조회 paths: /api/content/images/generate: post: tags: - Images summary: SNS 이미지 생성 요청 description: | 이벤트 정보를 기반으로 3가지 스타일의 SNS 이미지 생성을 비동기로 요청합니다. ## 처리 방식 - **비동기 처리**: Kafka image-job 토픽에 Job 발행 - **폴링 조회**: jobId로 생성 상태 조회 (GET /api/content/images/{jobId}) - **캐싱**: 동일한 eventDraftId 재요청 시 캐시 반환 (TTL 7일) ## 생성 스타일 1. **심플 스타일**: 깔끔한 디자인, 텍스트 중심 2. **화려한 스타일**: 눈에 띄는 디자인, 풍부한 색상 3. **트렌디 스타일**: 최신 트렌드, MZ세대 타겟 ## Resilience 패턴 - Circuit Breaker (실패율 50% 초과 시 Open) - Fallback (Stable Diffusion → DALL-E → 기본 템플릿) - Timeout (20초) operationId: 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/{jobId}: get: tags: - Images summary: 이미지 생성 상태 및 결과 조회 description: | jobId로 이미지 생성 상태를 조회합니다. ## 폴링 권장사항 - **폴링 간격**: 2초 - **최대 폴링 시간**: 30초 - **Timeout 후 처리**: 에러 메시지 표시 및 재시도 옵션 제공 ## 상태 종류 - **PENDING**: 대기 중 (Kafka Queue에서 대기) - **PROCESSING**: 생성 중 (AI API 호출 진행) - **COMPLETED**: 완료 (3가지 이미지 URL 반환) - **FAILED**: 실패 (에러 메시지 포함) ## 캐싱 - COMPLETED 상태는 Redis 캐싱 (TTL 7일) - 동일한 eventDraftId 재요청 시 즉시 반환 operationId: getImageGenerationStatus 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: [] components: securitySchemes: BearerAuth: type: http scheme: bearer bearerFormat: JWT description: JWT 토큰을 Authorization 헤더에 포함 (Bearer {token}) 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