mirror of
https://github.com/ktds-dg0501/kt-event-marketing-fe.git
synced 2025-12-06 12:16:24 +00:00
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1159 lines
36 KiB
YAML
1159 lines
36 KiB
YAML
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: Digital Garage Team
|
||
email: support@kt-event-marketing.com
|
||
|
||
servers:
|
||
- url: http://localhost:8082
|
||
description: Local Development Server
|
||
- url: https://dev-api.kt-event-marketing.com/content/v1
|
||
description: Development Server
|
||
- url: https://api.kt-event-marketing.com/content/v1
|
||
description: Production Server
|
||
|
||
tags:
|
||
- name: Job Status
|
||
description: 이미지 생성 작업 상태 조회 (비동기 폴링)
|
||
- name: Content Management
|
||
description: 생성된 콘텐츠 조회 및 관리 (UFR-CONT-020)
|
||
- name: Image Management
|
||
description: 이미지 재생성 및 삭제 (UFR-CONT-020)
|
||
|
||
paths:
|
||
/content/images/generate:
|
||
post:
|
||
tags:
|
||
- Job Status
|
||
summary: SNS 이미지 생성 요청 (비동기)
|
||
description: |
|
||
이벤트 정보를 기반으로 3가지 스타일의 SNS 이미지 생성을 비동기로 요청합니다.
|
||
|
||
## 처리 방식
|
||
- **비동기 처리**: Kafka `image-generation-job` 토픽에 Job 발행
|
||
- **폴링 조회**: jobId로 생성 상태 조회 (GET /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: []
|
||
|
||
/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: []
|
||
|
||
/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: []
|
||
|
||
/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: []
|
||
|
||
/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: []
|
||
|
||
/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
|