7개 마이크로서비스 API 설계 완료

- User Service API (7 APIs, 31KB)
- Event Service API (14 APIs, 41KB)
- AI Service API (3 APIs, 26KB)
- Content Service API (6 APIs, 37KB)
- Distribution Service API (2 APIs, 21KB)
- Participation Service API (5 APIs, 25KB)
- Analytics Service API (4 APIs, 28KB)

총 41개 API 엔드포인트, 6,912줄, OpenAPI 3.0 표준 준수
유저스토리 기반 설계, JWT 인증, Kafka/Redis 통합 문서화
API 설계서 작성 완료 (종합 가이드 포함)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
cherry2250
2025-10-23 16:56:54 +09:00
parent 781c3944ed
commit b9745f24e5
8 changed files with 5019 additions and 3323 deletions
+639 -29
View File
@@ -1,61 +1,91 @@
openapi: 3.0.3
info:
title: Content Service API
version: 1.0.0
description: |
KT AI 기반 소상공인 이벤트 자동 생성 서비스 - Content Service API
# KT AI 기반 소상공인 이벤트 자동 생성 서비스 - Content Service API
## 주요 기능
- SNS 이미지 생성 (UFR-CONT-010)
- 3가지 스타일 이미지 자동 생성 (심플, 화려한, 트렌디)
- AI 기반 이미지 생성 (Stable Diffusion / DALL-E)
- Circuit Breaker 및 Fallback 패턴 적용
- Redis 캐싱 (TTL 7일)
- **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 처리
- 폴링 방식으로 결과 조회
- Event Service와 느슨한 결합
## 비동기 처리 아키텍처
### 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 반환
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
- 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: Images
description: SNS 이미지 생성 및 조회
- 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:
- Images
summary: SNS 이미지 생성 요청
- Job Status
summary: SNS 이미지 생성 요청 (비동기)
description: |
이벤트 정보를 기반으로 3가지 스타일의 SNS 이미지 생성을 비동기로 요청합니다.
## 처리 방식
- **비동기 처리**: Kafka image-job 토픽에 Job 발행
- **폴링 조회**: jobId로 생성 상태 조회 (GET /api/content/images/{jobId})
- **비동기 처리**: Kafka `image-generation-job` 토픽에 Job 발행
- **폴링 조회**: jobId로 생성 상태 조회 (GET /api/content/images/jobs/{jobId})
- **캐싱**: 동일한 eventDraftId 재요청 시 캐시 반환 (TTL 7일)
## 생성 스타일
1. **심플 스타일**: 깔끔한 디자인, 텍스트 중심
2. **화려한 스타일**: 눈에 띄는 디자인, 풍부한 색상
3. **트렌디 스타일**: 최신 트렌드, MZ세대 타겟
1. **심플 스타일 (SIMPLE)**: 깔끔한 디자인, 텍스트 중심
2. **화려한 스타일 (FANCY)**: 눈에 띄는 디자인, 풍부한 색상
3. **트렌디 스타일 (TRENDY)**: 최신 트렌드, MZ세대 타겟
## Resilience 패턴
- Circuit Breaker (실패율 50% 초과 시 Open)
- Circuit Breaker (5분 타임아웃, 실패율 50% 초과 시 Open)
- Fallback (Stable Diffusion → DALL-E → 기본 템플릿)
- Timeout (20초)
operationId: generateImages
x-user-story: UFR-CONT-010
x-controller: ImageGenerationController.generateImages
requestBody:
required: true
content:
@@ -152,11 +182,11 @@ paths:
security:
- BearerAuth: []
/api/content/images/{jobId}:
/api/content/images/jobs/{jobId}:
get:
tags:
- Images
summary: 이미지 생성 상태 및 결과 조회
- Job Status
summary: 이미지 생성 작업 상태 조회 (폴링)
description: |
jobId로 이미지 생성 상태를 조회합니다.
@@ -169,13 +199,15 @@ paths:
- **PENDING**: 대기 중 (Kafka Queue에서 대기)
- **PROCESSING**: 생성 중 (AI API 호출 진행)
- **COMPLETED**: 완료 (3가지 이미지 URL 반환)
- **FAILED**: 실패 (에러 메시지 포함)
- **FAILED**: 실패 (에러 메시지 포함, Fallback 이미지 제공)
## 캐싱
- COMPLETED 상태는 Redis 캐싱 (TTL 7일)
- 동일한 eventDraftId 재요청 시 즉시 반환
operationId: getImageGenerationStatus
x-user-story: UFR-CONT-010
x-controller: ImageGenerationController.getJobStatus
parameters:
- name: jobId
in: path
@@ -307,6 +339,337 @@ paths:
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:
@@ -316,6 +679,90 @@ components:
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:
@@ -546,3 +993,166 @@ components:
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