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:
parent
781c3944ed
commit
b9745f24e5
665
design/backend/api/API-설계서.md
Normal file
665
design/backend/api/API-설계서.md
Normal file
@ -0,0 +1,665 @@
|
|||||||
|
# KT AI 기반 소상공인 이벤트 자동 생성 서비스 - API 설계서
|
||||||
|
|
||||||
|
## 문서 정보
|
||||||
|
- **작성일**: 2025-10-23
|
||||||
|
- **버전**: 1.0
|
||||||
|
- **작성자**: System Architect
|
||||||
|
- **관련 문서**:
|
||||||
|
- [유저스토리](../../userstory.md)
|
||||||
|
- [논리 아키텍처](../logical/logical-architecture.md)
|
||||||
|
- [외부 시퀀스 설계](../sequence/outer/)
|
||||||
|
- [내부 시퀀스 설계](../sequence/inner/)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 목차
|
||||||
|
1. [개요](#1-개요)
|
||||||
|
2. [API 설계 원칙](#2-api-설계-원칙)
|
||||||
|
3. [서비스별 API 명세](#3-서비스별-api-명세)
|
||||||
|
4. [API 통합 가이드](#4-api-통합-가이드)
|
||||||
|
5. [보안 및 인증](#5-보안-및-인증)
|
||||||
|
6. [에러 처리](#6-에러-처리)
|
||||||
|
7. [API 테스트 가이드](#7-api-테스트-가이드)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 개요
|
||||||
|
|
||||||
|
### 1.1 설계 범위
|
||||||
|
본 API 설계서는 KT AI 기반 소상공인 이벤트 자동 생성 서비스의 7개 마이크로서비스 API를 정의합니다.
|
||||||
|
|
||||||
|
### 1.2 마이크로서비스 구성
|
||||||
|
1. **User Service**: 사용자 인증 및 매장정보 관리
|
||||||
|
2. **Event Service**: 이벤트 전체 생명주기 관리
|
||||||
|
3. **AI Service**: AI 기반 이벤트 추천
|
||||||
|
4. **Content Service**: SNS 콘텐츠 생성
|
||||||
|
5. **Distribution Service**: 다중 채널 배포 관리
|
||||||
|
6. **Participation Service**: 이벤트 참여 및 당첨자 관리
|
||||||
|
7. **Analytics Service**: 실시간 효과 측정 및 통합 대시보드
|
||||||
|
|
||||||
|
### 1.3 파일 구조
|
||||||
|
```
|
||||||
|
design/backend/api/
|
||||||
|
├── user-service-api.yaml (31KB, 1,011 lines)
|
||||||
|
├── event-service-api.yaml (41KB, 1,373 lines)
|
||||||
|
├── ai-service-api.yaml (26KB, 847 lines)
|
||||||
|
├── content-service-api.yaml (37KB, 1,158 lines)
|
||||||
|
├── distribution-service-api.yaml (21KB, 653 lines)
|
||||||
|
├── participation-service-api.yaml (25KB, 820 lines)
|
||||||
|
├── analytics-service-api.yaml (28KB, 1,050 lines)
|
||||||
|
└── API-설계서.md (this file)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. API 설계 원칙
|
||||||
|
|
||||||
|
### 2.1 OpenAPI 3.0 표준 준수
|
||||||
|
- 모든 API는 OpenAPI 3.0 스펙을 따릅니다
|
||||||
|
- Swagger UI/Editor에서 직접 테스트 가능합니다
|
||||||
|
- 자동 코드 생성 및 문서화를 지원합니다
|
||||||
|
|
||||||
|
### 2.2 RESTful 설계
|
||||||
|
- **리소스 중심 URL 구조**: `/api/{resource}/{id}`
|
||||||
|
- **HTTP 메서드**: GET (조회), POST (생성), PUT (수정), DELETE (삭제)
|
||||||
|
- **상태 코드**: 200 (성공), 201 (생성), 400 (잘못된 요청), 401 (인증 실패), 403 (권한 없음), 404 (리소스 없음), 500 (서버 오류)
|
||||||
|
|
||||||
|
### 2.3 유저스토리 기반 설계
|
||||||
|
- 각 API 엔드포인트는 유저스토리와 매핑됩니다
|
||||||
|
- **x-user-story** 필드로 유저스토리 ID를 명시합니다
|
||||||
|
- **x-controller** 필드로 담당 컨트롤러를 명시합니다
|
||||||
|
|
||||||
|
### 2.4 서비스 독립성
|
||||||
|
- 각 서비스는 독립적인 OpenAPI 명세를 가집니다
|
||||||
|
- 공통 스키마는 각 서비스에서 필요에 따라 정의합니다
|
||||||
|
- 서비스 간 통신은 REST API, Kafka 이벤트, Redis 캐시를 통해 이루어집니다
|
||||||
|
|
||||||
|
### 2.5 Example 데이터 제공
|
||||||
|
- 모든 스키마에 example 데이터가 포함됩니다
|
||||||
|
- Swagger UI에서 즉시 테스트 가능합니다
|
||||||
|
- 성공/실패 시나리오 모두 포함합니다
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 서비스별 API 명세
|
||||||
|
|
||||||
|
### 3.1 User Service (사용자 인증 및 매장정보 관리)
|
||||||
|
|
||||||
|
**파일**: `user-service-api.yaml`
|
||||||
|
**관련 유저스토리**: UFR-USER-010, 020, 030, 040
|
||||||
|
|
||||||
|
#### API 엔드포인트 (7개)
|
||||||
|
|
||||||
|
| 메서드 | 경로 | 설명 | 유저스토리 | 인증 |
|
||||||
|
|--------|------|------|-----------|------|
|
||||||
|
| POST | /api/users/register | 회원가입 | UFR-USER-010 | - |
|
||||||
|
| POST | /api/users/login | 로그인 | UFR-USER-020 | - |
|
||||||
|
| POST | /api/users/logout | 로그아웃 | UFR-USER-040 | JWT |
|
||||||
|
| GET | /api/users/profile | 프로필 조회 | UFR-USER-030 | JWT |
|
||||||
|
| PUT | /api/users/profile | 프로필 수정 | UFR-USER-030 | JWT |
|
||||||
|
| PUT | /api/users/password | 비밀번호 변경 | UFR-USER-030 | JWT |
|
||||||
|
| GET | /api/users/{userId}/store | 매장정보 조회 (서비스 연동용) | - | JWT |
|
||||||
|
|
||||||
|
#### 주요 기능
|
||||||
|
- JWT 토큰 기반 인증 (TTL 7일)
|
||||||
|
- 사업자번호 검증 (국세청 API 연동)
|
||||||
|
- Redis 세션 관리
|
||||||
|
- BCrypt 비밀번호 해싱
|
||||||
|
- AES-256-GCM 사업자번호 암호화
|
||||||
|
|
||||||
|
#### 주요 스키마
|
||||||
|
- `UserRegisterRequest`: 회원가입 요청
|
||||||
|
- `UserLoginRequest`: 로그인 요청
|
||||||
|
- `UserProfileResponse`: 프로필 응답
|
||||||
|
- `StoreInfoResponse`: 매장정보 응답
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.2 Event Service (이벤트 전체 생명주기 관리)
|
||||||
|
|
||||||
|
**파일**: `event-service-api.yaml`
|
||||||
|
**관련 유저스토리**: UFR-EVENT-010 ~ 070
|
||||||
|
|
||||||
|
#### API 엔드포인트 (14개)
|
||||||
|
|
||||||
|
**Dashboard & Event List:**
|
||||||
|
| 메서드 | 경로 | 설명 | 유저스토리 | 인증 |
|
||||||
|
|--------|------|------|-----------|------|
|
||||||
|
| GET | /api/events | 이벤트 목록 조회 | UFR-EVENT-010, 070 | JWT |
|
||||||
|
| GET | /api/events/{eventId} | 이벤트 상세 조회 | UFR-EVENT-060 | JWT |
|
||||||
|
|
||||||
|
**Event Creation Flow (5 Steps):**
|
||||||
|
| 메서드 | 경로 | 설명 | 유저스토리 | 인증 |
|
||||||
|
|--------|------|------|-----------|------|
|
||||||
|
| POST | /api/events/objectives | Step 1: 이벤트 목적 선택 | UFR-EVENT-020 | JWT |
|
||||||
|
| POST | /api/events/{eventId}/ai-recommendations | Step 2: AI 추천 요청 | UFR-EVENT-030 | JWT |
|
||||||
|
| PUT | /api/events/{eventId}/recommendations | Step 2-2: AI 추천 선택 | UFR-EVENT-030 | JWT |
|
||||||
|
| POST | /api/events/{eventId}/images | Step 3: 이미지 생성 요청 | UFR-CONT-010 | JWT |
|
||||||
|
| PUT | /api/events/{eventId}/images/{imageId}/select | Step 3-2: 이미지 선택 | UFR-CONT-010 | JWT |
|
||||||
|
| PUT | /api/events/{eventId}/images/{imageId}/edit | Step 3-3: 이미지 편집 | UFR-CONT-020 | JWT |
|
||||||
|
| PUT | /api/events/{eventId}/channels | Step 4: 배포 채널 선택 | UFR-EVENT-040 | JWT |
|
||||||
|
| POST | /api/events/{eventId}/publish | Step 5: 최종 승인 및 배포 | UFR-EVENT-050 | JWT |
|
||||||
|
|
||||||
|
**Event Management:**
|
||||||
|
| 메서드 | 경로 | 설명 | 유저스토리 | 인증 |
|
||||||
|
|--------|------|------|-----------|------|
|
||||||
|
| PUT | /api/events/{eventId} | 이벤트 수정 | UFR-EVENT-060 | JWT |
|
||||||
|
| DELETE | /api/events/{eventId} | 이벤트 삭제 | UFR-EVENT-070 | JWT |
|
||||||
|
| POST | /api/events/{eventId}/end | 이벤트 조기 종료 | UFR-EVENT-060 | JWT |
|
||||||
|
|
||||||
|
**Job Status:**
|
||||||
|
| 메서드 | 경로 | 설명 | 유저스토리 | 인증 |
|
||||||
|
|--------|------|------|-----------|------|
|
||||||
|
| GET | /api/jobs/{jobId} | Job 상태 폴링 | UFR-EVENT-030, UFR-CONT-010 | JWT |
|
||||||
|
|
||||||
|
#### 주요 기능
|
||||||
|
- 이벤트 생명주기 관리 (DRAFT → PUBLISHED → ENDED)
|
||||||
|
- Kafka Job 발행 (ai-event-generation-job, image-generation-job)
|
||||||
|
- Kafka Event 발행 (EventCreated)
|
||||||
|
- Distribution Service 동기 호출
|
||||||
|
- Redis 기반 AI/이미지 데이터 캐싱
|
||||||
|
- Job 상태 폴링 메커니즘 (PENDING, PROCESSING, COMPLETED, FAILED)
|
||||||
|
|
||||||
|
#### 주요 스키마
|
||||||
|
- `EventObjectiveRequest`: 이벤트 목적 선택
|
||||||
|
- `EventResponse`: 이벤트 응답
|
||||||
|
- `JobStatusResponse`: Job 상태 응답
|
||||||
|
- `AIRecommendationSelection`: AI 추천 선택
|
||||||
|
- `ChannelSelectionRequest`: 배포 채널 선택
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.3 AI Service (AI 기반 이벤트 추천)
|
||||||
|
|
||||||
|
**파일**: `ai-service-api.yaml`
|
||||||
|
**관련 유저스토리**: UFR-AI-010
|
||||||
|
|
||||||
|
#### API 엔드포인트 (3개)
|
||||||
|
|
||||||
|
| 메서드 | 경로 | 설명 | 유저스토리 | 인증 |
|
||||||
|
|--------|------|------|-----------|------|
|
||||||
|
| GET | /health | 서비스 헬스체크 | - | - |
|
||||||
|
| GET | /internal/jobs/{jobId}/status | Job 상태 조회 (내부 API) | UFR-AI-010 | JWT |
|
||||||
|
| GET | /internal/recommendations/{eventId} | AI 추천 결과 조회 (내부 API) | UFR-AI-010 | JWT |
|
||||||
|
|
||||||
|
#### Kafka Consumer (비동기 처리)
|
||||||
|
- **Topic**: `ai-event-generation-job`
|
||||||
|
- **Consumer Group**: `ai-service-consumers`
|
||||||
|
- **처리 시간**: 최대 5분
|
||||||
|
- **결과 저장**: Redis (TTL 24시간)
|
||||||
|
|
||||||
|
#### 주요 기능
|
||||||
|
- 업종/지역/시즌 트렌드 분석
|
||||||
|
- 3가지 차별화된 이벤트 기획안 생성
|
||||||
|
- 예상 성과 계산 (참여자 수, ROI, 매출 증가율)
|
||||||
|
- Circuit Breaker 패턴 (5분 timeout, 캐시 fallback)
|
||||||
|
- Claude API / GPT-4 API 연동
|
||||||
|
|
||||||
|
#### 주요 스키마
|
||||||
|
- `KafkaAIJobMessage`: Kafka Job 입력
|
||||||
|
- `AIRecommendationResult`: AI 추천 결과 (트렌드 분석 + 3가지 옵션)
|
||||||
|
- `TrendAnalysis`: 업종/지역/시즌 트렌드
|
||||||
|
- `EventRecommendation`: 이벤트 기획안 (컨셉, 경품, 참여방법, 예상성과)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.4 Content Service (SNS 콘텐츠 생성)
|
||||||
|
|
||||||
|
**파일**: `content-service-api.yaml`
|
||||||
|
**관련 유저스토리**: UFR-CONT-010, 020
|
||||||
|
|
||||||
|
#### API 엔드포인트 (6개)
|
||||||
|
|
||||||
|
| 메서드 | 경로 | 설명 | 유저스토리 | 인증 |
|
||||||
|
|--------|------|------|-----------|------|
|
||||||
|
| POST | /api/content/images/generate | 이미지 생성 요청 (비동기) | UFR-CONT-010 | JWT |
|
||||||
|
| GET | /api/content/images/jobs/{jobId} | Job 상태 폴링 | UFR-CONT-010 | JWT |
|
||||||
|
| GET | /api/content/events/{eventDraftId} | 이벤트 전체 콘텐츠 조회 | UFR-CONT-020 | JWT |
|
||||||
|
| GET | /api/content/events/{eventDraftId}/images | 이미지 목록 조회 | UFR-CONT-020 | JWT |
|
||||||
|
| GET | /api/content/images/{imageId} | 이미지 상세 조회 | UFR-CONT-020 | JWT |
|
||||||
|
| POST | /api/content/images/{imageId}/regenerate | 이미지 재생성 | UFR-CONT-020 | JWT |
|
||||||
|
|
||||||
|
#### Kafka Consumer (비동기 처리)
|
||||||
|
- **Topic**: `image-generation-job`
|
||||||
|
- **Consumer Group**: `content-service-consumers`
|
||||||
|
- **처리 시간**: 최대 5분
|
||||||
|
- **결과 저장**: Redis (CDN URL, TTL 7일)
|
||||||
|
|
||||||
|
#### 주요 기능
|
||||||
|
- 3가지 스타일 이미지 생성 (SIMPLE, FANCY, TRENDY)
|
||||||
|
- 플랫폼별 최적화 (Instagram 1080x1080, Naver 800x600, Kakao 800x800)
|
||||||
|
- Circuit Breaker 패턴 (Stable Diffusion → DALL-E → Default Template)
|
||||||
|
- Azure Blob Storage (CDN) 연동
|
||||||
|
- Redis 기반 AI 데이터 읽기
|
||||||
|
|
||||||
|
#### 주요 스키마
|
||||||
|
- `ImageGenerationJob`: Kafka Job 입력
|
||||||
|
- `ImageGenerationRequest`: 이미지 생성 요청
|
||||||
|
- `GeneratedImage`: 생성된 이미지 (style, platform, CDN URL)
|
||||||
|
- `ContentResponse`: 전체 콘텐츠 응답
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.5 Distribution Service (다중 채널 배포 관리)
|
||||||
|
|
||||||
|
**파일**: `distribution-service-api.yaml`
|
||||||
|
**관련 유저스토리**: UFR-DIST-010, 020
|
||||||
|
|
||||||
|
#### API 엔드포인트 (2개)
|
||||||
|
|
||||||
|
| 메서드 | 경로 | 설명 | 유저스토리 | 인증 |
|
||||||
|
|--------|------|------|-----------|------|
|
||||||
|
| POST | /api/distribution/distribute | 다중 채널 배포 (동기) | UFR-DIST-010 | JWT |
|
||||||
|
| GET | /api/distribution/{eventId}/status | 배포 상태 조회 | UFR-DIST-020 | JWT |
|
||||||
|
|
||||||
|
#### 주요 기능
|
||||||
|
- **배포 채널**: 우리동네TV, 링고비즈, 지니TV, Instagram, Naver Blog, Kakao Channel
|
||||||
|
- **병렬 배포**: 6개 채널 동시 배포 (1분 이내)
|
||||||
|
- **Resilience 패턴**:
|
||||||
|
- Circuit Breaker: 채널별 독립 적용
|
||||||
|
- Retry: 최대 3회 재시도 (지수 백오프: 1s, 2s, 4s)
|
||||||
|
- Bulkhead: 채널별 스레드 풀 격리
|
||||||
|
- Fallback: 실패 채널 스킵 + 알림
|
||||||
|
- **Kafka Event 발행**: DistributionCompleted
|
||||||
|
- **로깅**: Event DB에 distribution_logs 저장
|
||||||
|
|
||||||
|
#### 주요 스키마
|
||||||
|
- `DistributionRequest`: 배포 요청
|
||||||
|
- `DistributionResponse`: 배포 응답 (채널별 결과)
|
||||||
|
- `DistributionStatusResponse`: 배포 상태
|
||||||
|
- `ChannelDistributionResult`: 채널별 배포 결과
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.6 Participation Service (이벤트 참여 및 당첨자 관리)
|
||||||
|
|
||||||
|
**파일**: `participation-service-api.yaml`
|
||||||
|
**관련 유저스토리**: UFR-PART-010, 020, 030
|
||||||
|
|
||||||
|
#### API 엔드포인트 (5개)
|
||||||
|
|
||||||
|
| 메서드 | 경로 | 설명 | 유저스토리 | 인증 |
|
||||||
|
|--------|------|------|-----------|------|
|
||||||
|
| POST | /api/events/{eventId}/participate | 이벤트 참여 | UFR-PART-010 | - |
|
||||||
|
| GET | /api/events/{eventId}/participants | 참여자 목록 조회 | UFR-PART-020 | JWT |
|
||||||
|
| GET | /api/events/{eventId}/participants/{participantId} | 참여자 상세 조회 | UFR-PART-020 | JWT |
|
||||||
|
| POST | /api/events/{eventId}/draw-winners | 당첨자 추첨 | UFR-PART-030 | JWT |
|
||||||
|
| GET | /api/events/{eventId}/winners | 당첨자 목록 조회 | UFR-PART-030 | JWT |
|
||||||
|
|
||||||
|
#### 주요 기능
|
||||||
|
- 중복 참여 체크 (전화번호 기반)
|
||||||
|
- 매장 방문 고객 가산점 적용
|
||||||
|
- 난수 기반 무작위 추첨
|
||||||
|
- Kafka Event 발행 (ParticipantRegistered)
|
||||||
|
- 개인정보 수집/이용 동의 관리
|
||||||
|
- 페이지네이션 지원
|
||||||
|
|
||||||
|
#### 주요 스키마
|
||||||
|
- `ParticipationRequest`: 참여 요청
|
||||||
|
- `ParticipationResponse`: 참여 응답 (응모번호)
|
||||||
|
- `ParticipantListResponse`: 참여자 목록
|
||||||
|
- `WinnerDrawRequest`: 당첨자 추첨 요청
|
||||||
|
- `WinnerResponse`: 당첨자 정보
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3.7 Analytics Service (실시간 효과 측정 및 통합 대시보드)
|
||||||
|
|
||||||
|
**파일**: `analytics-service-api.yaml`
|
||||||
|
**관련 유저스토리**: UFR-ANAL-010
|
||||||
|
|
||||||
|
#### API 엔드포인트 (4개)
|
||||||
|
|
||||||
|
| 메서드 | 경로 | 설명 | 유저스토리 | 인증 |
|
||||||
|
|--------|------|------|-----------|------|
|
||||||
|
| GET | /api/events/{eventId}/analytics | 성과 대시보드 조회 | UFR-ANAL-010 | JWT |
|
||||||
|
| GET | /api/events/{eventId}/analytics/channels | 채널별 성과 분석 | UFR-ANAL-010 | JWT |
|
||||||
|
| GET | /api/events/{eventId}/analytics/timeline | 시간대별 참여 추이 | UFR-ANAL-010 | JWT |
|
||||||
|
| GET | /api/events/{eventId}/analytics/roi | 투자 대비 수익률 상세 | UFR-ANAL-010 | JWT |
|
||||||
|
|
||||||
|
#### Kafka Event 구독
|
||||||
|
- **EventCreated**: 이벤트 기본 통계 초기화
|
||||||
|
- **ParticipantRegistered**: 실시간 참여자 수 증가
|
||||||
|
- **DistributionCompleted**: 배포 채널 통계 업데이트
|
||||||
|
|
||||||
|
#### 주요 기능
|
||||||
|
- **실시간 대시보드**: Redis 캐싱 (TTL 5분)
|
||||||
|
- **외부 API 통합**: 우리동네TV, 지니TV, SNS APIs (조회수, 노출수, 소셜 인터랙션)
|
||||||
|
- **Circuit Breaker**: 외부 API 실패 시 캐시 fallback
|
||||||
|
- **ROI 계산**: 비용 대비 수익률 자동 계산
|
||||||
|
- **성과 집계**: 채널별, 시간대별 성과 분석
|
||||||
|
|
||||||
|
#### 주요 스키마
|
||||||
|
- `AnalyticsDashboardResponse`: 대시보드 전체 데이터
|
||||||
|
- `ChannelPerformanceResponse`: 채널별 성과
|
||||||
|
- `TimelineDataResponse`: 시간대별 참여 추이
|
||||||
|
- `RoiDetailResponse`: ROI 상세 분석
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. API 통합 가이드
|
||||||
|
|
||||||
|
### 4.1 이벤트 생성 플로우 (Event-Driven)
|
||||||
|
|
||||||
|
```
|
||||||
|
1. 이벤트 목적 선택 (Event Service)
|
||||||
|
POST /api/events/objectives
|
||||||
|
→ EventCreated 이벤트 발행 (Kafka)
|
||||||
|
→ Analytics Service 구독 (통계 초기화)
|
||||||
|
|
||||||
|
2. AI 추천 요청 (Event Service → AI Service)
|
||||||
|
POST /api/events/{eventId}/ai-recommendations
|
||||||
|
→ ai-event-generation-job 발행 (Kafka)
|
||||||
|
→ AI Service 구독 및 처리 (비동기)
|
||||||
|
→ Redis에 결과 저장 (TTL 24시간)
|
||||||
|
→ 클라이언트 폴링: GET /api/jobs/{jobId}
|
||||||
|
|
||||||
|
3. AI 추천 선택 (Event Service)
|
||||||
|
PUT /api/events/{eventId}/recommendations
|
||||||
|
→ Redis에서 AI 추천 데이터 읽기
|
||||||
|
→ Event DB에 선택된 추천 저장
|
||||||
|
|
||||||
|
4. 이미지 생성 요청 (Event Service → Content Service)
|
||||||
|
POST /api/events/{eventId}/images
|
||||||
|
→ image-generation-job 발행 (Kafka)
|
||||||
|
→ Content Service 구독 및 처리 (비동기)
|
||||||
|
→ Redis에서 AI 데이터 읽기
|
||||||
|
→ CDN에 이미지 업로드
|
||||||
|
→ Redis에 CDN URL 저장 (TTL 7일)
|
||||||
|
→ 클라이언트 폴링: GET /api/jobs/{jobId}
|
||||||
|
|
||||||
|
5. 이미지 선택 및 편집 (Event Service)
|
||||||
|
PUT /api/events/{eventId}/images/{imageId}/select
|
||||||
|
PUT /api/events/{eventId}/images/{imageId}/edit
|
||||||
|
|
||||||
|
6. 배포 채널 선택 (Event Service)
|
||||||
|
PUT /api/events/{eventId}/channels
|
||||||
|
|
||||||
|
7. 최종 승인 및 배포 (Event Service → Distribution Service)
|
||||||
|
POST /api/events/{eventId}/publish
|
||||||
|
→ Distribution Service 동기 호출: POST /api/distribution/distribute
|
||||||
|
→ 다중 채널 병렬 배포 (1분 이내)
|
||||||
|
→ DistributionCompleted 이벤트 발행 (Kafka)
|
||||||
|
→ Analytics Service 구독 (배포 통계 업데이트)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 고객 참여 플로우 (Event-Driven)
|
||||||
|
|
||||||
|
```
|
||||||
|
1. 이벤트 참여 (Participation Service)
|
||||||
|
POST /api/events/{eventId}/participate
|
||||||
|
→ 중복 참여 체크
|
||||||
|
→ Participation DB 저장
|
||||||
|
→ ParticipantRegistered 이벤트 발행 (Kafka)
|
||||||
|
→ Analytics Service 구독 (참여자 수 실시간 증가)
|
||||||
|
|
||||||
|
2. 당첨자 추첨 (Participation Service)
|
||||||
|
POST /api/events/{eventId}/draw-winners
|
||||||
|
→ 난수 기반 무작위 추첨
|
||||||
|
→ Winners DB 저장
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 성과 분석 플로우 (Event-Driven)
|
||||||
|
|
||||||
|
```
|
||||||
|
1. 실시간 대시보드 조회 (Analytics Service)
|
||||||
|
GET /api/events/{eventId}/analytics
|
||||||
|
→ Redis 캐시 확인 (TTL 5분)
|
||||||
|
→ 캐시 HIT: 즉시 반환
|
||||||
|
→ 캐시 MISS:
|
||||||
|
- Analytics DB 조회 (이벤트/참여 통계)
|
||||||
|
- 외부 APIs 조회 (우리동네TV, 지니TV, SNS) [Circuit Breaker]
|
||||||
|
- Redis 캐싱 후 반환
|
||||||
|
|
||||||
|
2. Kafka 이벤트 구독 (Analytics Service Background)
|
||||||
|
- EventCreated 구독 → 이벤트 기본 정보 초기화
|
||||||
|
- ParticipantRegistered 구독 → 참여자 수 실시간 증가
|
||||||
|
- DistributionCompleted 구독 → 배포 채널 통계 업데이트
|
||||||
|
- 캐시 무효화 → 다음 조회 시 최신 데이터 갱신
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.4 서비스 간 통신 패턴
|
||||||
|
|
||||||
|
| 패턴 | 사용 시나리오 | 통신 방식 | 예시 |
|
||||||
|
|------|-------------|----------|------|
|
||||||
|
| **동기 REST API** | 즉시 응답 필요 | HTTP/JSON | Distribution Service 배포 요청 |
|
||||||
|
| **Kafka Job Topics** | 장시간 비동기 작업 | Kafka 메시지 큐 | AI 추천, 이미지 생성 |
|
||||||
|
| **Kafka Event Topics** | 상태 변경 알림 | Kafka Pub/Sub | EventCreated, ParticipantRegistered |
|
||||||
|
| **Redis Cache** | 데이터 공유 | Redis Get/Set | AI 결과, 이미지 URL |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 보안 및 인증
|
||||||
|
|
||||||
|
### 5.1 JWT 기반 인증
|
||||||
|
|
||||||
|
**토큰 발급:**
|
||||||
|
- User Service에서 로그인/회원가입 시 JWT 토큰 발급
|
||||||
|
- 토큰 만료 시간: 7일
|
||||||
|
- Redis에 세션 정보 저장 (TTL 7일)
|
||||||
|
|
||||||
|
**토큰 검증:**
|
||||||
|
- API Gateway에서 모든 요청의 JWT 토큰 검증
|
||||||
|
- Authorization 헤더: `Bearer {token}`
|
||||||
|
- 검증 실패 시 401 Unauthorized 응답
|
||||||
|
|
||||||
|
**보호된 엔드포인트:**
|
||||||
|
- 모든 API (회원가입, 로그인, 이벤트 참여 제외)
|
||||||
|
|
||||||
|
### 5.2 민감 정보 암호화
|
||||||
|
|
||||||
|
- **비밀번호**: BCrypt 해싱 (Cost Factor: 10)
|
||||||
|
- **사업자번호**: AES-256-GCM 암호화
|
||||||
|
- **개인정보**: 전화번호 마스킹 (010-****-1234)
|
||||||
|
|
||||||
|
### 5.3 API Rate Limiting
|
||||||
|
|
||||||
|
- API Gateway에서 사용자당 100 req/min 제한
|
||||||
|
- Redis 기반 Rate Limiting 구현
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 에러 처리
|
||||||
|
|
||||||
|
### 6.1 표준 에러 응답 포맷
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"errorCode": "ERROR_CODE",
|
||||||
|
"message": "사용자 친화적인 에러 메시지",
|
||||||
|
"details": "상세 에러 정보 (선택)",
|
||||||
|
"timestamp": "2025-10-23T16:30:00Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 HTTP 상태 코드
|
||||||
|
|
||||||
|
| 상태 코드 | 설명 | 사용 예시 |
|
||||||
|
|----------|------|----------|
|
||||||
|
| 200 OK | 성공 | GET 요청 성공 |
|
||||||
|
| 201 Created | 생성 성공 | POST 요청으로 리소스 생성 |
|
||||||
|
| 400 Bad Request | 잘못된 요청 | 유효성 검증 실패 |
|
||||||
|
| 401 Unauthorized | 인증 실패 | JWT 토큰 없음/만료 |
|
||||||
|
| 403 Forbidden | 권한 없음 | 접근 권한 부족 |
|
||||||
|
| 404 Not Found | 리소스 없음 | 존재하지 않는 이벤트 조회 |
|
||||||
|
| 409 Conflict | 충돌 | 중복 참여, 동시성 문제 |
|
||||||
|
| 500 Internal Server Error | 서버 오류 | 서버 내부 오류 |
|
||||||
|
| 503 Service Unavailable | 서비스 불가 | Circuit Breaker Open |
|
||||||
|
|
||||||
|
### 6.3 서비스별 주요 에러 코드
|
||||||
|
|
||||||
|
**User Service:**
|
||||||
|
- `USER_001`: 중복 사용자
|
||||||
|
- `USER_002`: 사업자번호 검증 실패
|
||||||
|
- `USER_003`: 사용자 없음
|
||||||
|
- `AUTH_001`: 인증 실패
|
||||||
|
- `AUTH_002`: 유효하지 않은 토큰
|
||||||
|
|
||||||
|
**Event Service:**
|
||||||
|
- `EVENT_001`: 이벤트 없음
|
||||||
|
- `EVENT_002`: 유효하지 않은 상태 전환
|
||||||
|
- `EVENT_003`: 필수 데이터 누락 (AI 추천, 이미지)
|
||||||
|
- `JOB_001`: Job 없음
|
||||||
|
- `JOB_002`: Job 실패
|
||||||
|
|
||||||
|
**Participation Service:**
|
||||||
|
- `PART_001`: 중복 참여
|
||||||
|
- `PART_002`: 이벤트 기간 아님
|
||||||
|
- `PART_003`: 참여자 없음
|
||||||
|
|
||||||
|
**Distribution Service:**
|
||||||
|
- `DIST_001`: 배포 실패
|
||||||
|
- `DIST_002`: Circuit Breaker Open
|
||||||
|
|
||||||
|
**Analytics Service:**
|
||||||
|
- `ANALYTICS_001`: 데이터 없음
|
||||||
|
- `EXTERNAL_API_ERROR`: 외부 API 장애
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. API 테스트 가이드
|
||||||
|
|
||||||
|
### 7.1 Swagger UI를 통한 테스트
|
||||||
|
|
||||||
|
**방법 1: Swagger Editor**
|
||||||
|
1. https://editor.swagger.io/ 접속
|
||||||
|
2. 각 서비스의 YAML 파일 내용 붙여넣기
|
||||||
|
3. 우측 Swagger UI에서 API 테스트
|
||||||
|
|
||||||
|
**방법 2: SwaggerHub**
|
||||||
|
1. 각 API 명세의 `servers` 섹션에 SwaggerHub Mock Server URL 포함
|
||||||
|
2. Mock Server를 통한 즉시 테스트 가능
|
||||||
|
|
||||||
|
**방법 3: Redocly**
|
||||||
|
```bash
|
||||||
|
# 각 API 명세 검증
|
||||||
|
npx @redocly/cli lint design/backend/api/*.yaml
|
||||||
|
|
||||||
|
# 문서 HTML 생성
|
||||||
|
npx @redocly/cli build-docs design/backend/api/user-service-api.yaml \
|
||||||
|
--output docs/user-service-api.html
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.2 테스트 시나리오 예시
|
||||||
|
|
||||||
|
**1. 회원가입 → 로그인 → 이벤트 생성 플로우**
|
||||||
|
```bash
|
||||||
|
# 1. 회원가입
|
||||||
|
POST /api/users/register
|
||||||
|
{
|
||||||
|
"name": "김사장",
|
||||||
|
"phoneNumber": "010-1234-5678",
|
||||||
|
"email": "owner@example.com",
|
||||||
|
"password": "SecurePass123!",
|
||||||
|
"store": {
|
||||||
|
"name": "맛있는 고깃집",
|
||||||
|
"industry": "RESTAURANT",
|
||||||
|
"address": "서울시 강남구 테헤란로 123",
|
||||||
|
"businessNumber": "123-45-67890"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 2. 로그인
|
||||||
|
POST /api/users/login
|
||||||
|
{
|
||||||
|
"phoneNumber": "010-1234-5678",
|
||||||
|
"password": "SecurePass123!"
|
||||||
|
}
|
||||||
|
# → JWT 토큰 수신
|
||||||
|
|
||||||
|
# 3. 이벤트 목적 선택
|
||||||
|
POST /api/events/objectives
|
||||||
|
Authorization: Bearer {token}
|
||||||
|
{
|
||||||
|
"objective": "NEW_CUSTOMER_ACQUISITION"
|
||||||
|
}
|
||||||
|
# → eventId 수신
|
||||||
|
|
||||||
|
# 4. AI 추천 요청
|
||||||
|
POST /api/events/{eventId}/ai-recommendations
|
||||||
|
Authorization: Bearer {token}
|
||||||
|
# → jobId 수신
|
||||||
|
|
||||||
|
# 5. Job 상태 폴링 (5초 간격)
|
||||||
|
GET /api/jobs/{jobId}
|
||||||
|
Authorization: Bearer {token}
|
||||||
|
# → status: COMPLETED 확인
|
||||||
|
|
||||||
|
# 6. AI 추천 선택
|
||||||
|
PUT /api/events/{eventId}/recommendations
|
||||||
|
Authorization: Bearer {token}
|
||||||
|
{
|
||||||
|
"selectedOption": 1,
|
||||||
|
"customization": {
|
||||||
|
"title": "봄맞이 삼겹살 50% 할인 이벤트",
|
||||||
|
"prizeName": "삼겹살 1인분 무료"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ... (이미지 생성, 배포 채널 선택, 최종 승인)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.3 Mock 데이터 활용
|
||||||
|
|
||||||
|
- 모든 API 명세에 example 데이터 포함
|
||||||
|
- Swagger UI의 "Try it out" 기능으로 즉시 테스트
|
||||||
|
- 성공/실패 시나리오 모두 example 제공
|
||||||
|
|
||||||
|
### 7.4 통합 테스트 도구
|
||||||
|
|
||||||
|
**Postman Collection 생성:**
|
||||||
|
```bash
|
||||||
|
# OpenAPI 명세를 Postman Collection으로 변환
|
||||||
|
npx openapi-to-postmanv2 -s design/backend/api/user-service-api.yaml \
|
||||||
|
-o postman/user-service-collection.json
|
||||||
|
```
|
||||||
|
|
||||||
|
**Newman (CLI 테스트 실행):**
|
||||||
|
```bash
|
||||||
|
# Postman Collection 실행
|
||||||
|
newman run postman/user-service-collection.json \
|
||||||
|
--environment postman/dev-environment.json
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 부록
|
||||||
|
|
||||||
|
### A. 파일 통계
|
||||||
|
|
||||||
|
| 서비스 | 파일명 | 크기 | 라인 수 | API 수 |
|
||||||
|
|--------|--------|------|--------|--------|
|
||||||
|
| User | user-service-api.yaml | 31KB | 1,011 | 7 |
|
||||||
|
| Event | event-service-api.yaml | 41KB | 1,373 | 14 |
|
||||||
|
| AI | ai-service-api.yaml | 26KB | 847 | 3 |
|
||||||
|
| Content | content-service-api.yaml | 37KB | 1,158 | 6 |
|
||||||
|
| Distribution | distribution-service-api.yaml | 21KB | 653 | 2 |
|
||||||
|
| Participation | participation-service-api.yaml | 25KB | 820 | 5 |
|
||||||
|
| Analytics | analytics-service-api.yaml | 28KB | 1,050 | 4 |
|
||||||
|
| **합계** | - | **209KB** | **6,912** | **41** |
|
||||||
|
|
||||||
|
### B. 주요 의사결정
|
||||||
|
|
||||||
|
1. **OpenAPI 3.0 표준 채택**: 업계 표준 준수, 자동 코드 생성 지원
|
||||||
|
2. **서비스별 독립 명세**: 서비스 독립성 보장, 독립 배포 가능
|
||||||
|
3. **유저스토리 기반 설계**: x-user-story 필드로 추적성 확보
|
||||||
|
4. **Example 데이터 포함**: Swagger UI 즉시 테스트 가능
|
||||||
|
5. **JWT 인증 표준화**: 모든 서비스에서 일관된 인증 방식
|
||||||
|
6. **에러 응답 표준화**: 일관된 에러 응답 포맷
|
||||||
|
7. **Kafka + Redis 통합**: Event-Driven 아키텍처 지원
|
||||||
|
8. **Circuit Breaker 패턴**: 외부 API 장애 대응
|
||||||
|
|
||||||
|
### C. 다음 단계
|
||||||
|
|
||||||
|
1. **외부 시퀀스 설계**: 서비스 간 API 호출 흐름 상세 설계
|
||||||
|
2. **내부 시퀀스 설계**: 서비스 내부 컴포넌트 간 호출 흐름 설계
|
||||||
|
3. **클래스 설계**: 서비스별 클래스 다이어그램 작성
|
||||||
|
4. **데이터 설계**: 서비스별 데이터베이스 스키마 설계
|
||||||
|
5. **백엔드 개발**: OpenAPI 명세 기반 코드 생성 및 구현
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**문서 버전**: 1.0
|
||||||
|
**최종 수정일**: 2025-10-23
|
||||||
|
**작성자**: System Architect
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,61 +1,91 @@
|
|||||||
openapi: 3.0.3
|
openapi: 3.0.3
|
||||||
info:
|
info:
|
||||||
title: Content Service API
|
title: Content Service API
|
||||||
|
version: 1.0.0
|
||||||
description: |
|
description: |
|
||||||
KT AI 기반 소상공인 이벤트 자동 생성 서비스 - Content Service API
|
# KT AI 기반 소상공인 이벤트 자동 생성 서비스 - Content Service API
|
||||||
|
|
||||||
## 주요 기능
|
## 주요 기능
|
||||||
- SNS 이미지 생성 (UFR-CONT-010)
|
- **SNS 이미지 생성** (UFR-CONT-010): AI 기반 이벤트 이미지 자동 생성
|
||||||
- 3가지 스타일 이미지 자동 생성 (심플, 화려한, 트렌디)
|
- **콘텐츠 편집** (UFR-CONT-020): 생성된 이미지 조회, 재생성, 삭제
|
||||||
- AI 기반 이미지 생성 (Stable Diffusion / DALL-E)
|
- **3가지 스타일**: 심플(SIMPLE), 화려한(FANCY), 트렌디(TRENDY)
|
||||||
- Circuit Breaker 및 Fallback 패턴 적용
|
- **3개 플랫폼 최적화**: Instagram (1080x1080), Naver (800x600), Kakao (800x800)
|
||||||
- Redis 캐싱 (TTL 7일)
|
- **Redis 캐싱**: TTL 7일, 동일 eventDraftId 재요청 시 캐시 반환
|
||||||
|
- **CDN 이미지 저장**: Azure Blob Storage 기반
|
||||||
|
|
||||||
## 비동기 처리 방식
|
## 비동기 처리 아키텍처
|
||||||
- Kafka 기반 Job 처리
|
|
||||||
- 폴링 방식으로 결과 조회
|
### Kafka Job Consumer
|
||||||
- Event Service와 느슨한 결합
|
**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:
|
contact:
|
||||||
name: Content Service Team
|
name: Content Service Team
|
||||||
email: content-team@kt.com
|
email: content-team@kt.com
|
||||||
|
|
||||||
servers:
|
servers:
|
||||||
- url: https://api.kt-event.com/v1
|
- url: http://localhost:8083
|
||||||
description: Production Server
|
description: Content Service Local Development Server
|
||||||
- url: https://api-dev.kt-event.com/v1
|
- url: https://api-dev.kt-event.com
|
||||||
description: Development Server
|
description: Development Server
|
||||||
|
- url: https://api.kt-event.com
|
||||||
|
description: Production Server
|
||||||
|
|
||||||
tags:
|
tags:
|
||||||
- name: Images
|
- name: Job Status
|
||||||
description: SNS 이미지 생성 및 조회
|
description: 이미지 생성 작업 상태 조회 (비동기 폴링)
|
||||||
|
- name: Content Management
|
||||||
|
description: 생성된 콘텐츠 조회 및 관리 (UFR-CONT-020)
|
||||||
|
- name: Image Management
|
||||||
|
description: 이미지 재생성 및 삭제 (UFR-CONT-020)
|
||||||
|
|
||||||
paths:
|
paths:
|
||||||
/api/content/images/generate:
|
/api/content/images/generate:
|
||||||
post:
|
post:
|
||||||
tags:
|
tags:
|
||||||
- Images
|
- Job Status
|
||||||
summary: SNS 이미지 생성 요청
|
summary: SNS 이미지 생성 요청 (비동기)
|
||||||
description: |
|
description: |
|
||||||
이벤트 정보를 기반으로 3가지 스타일의 SNS 이미지 생성을 비동기로 요청합니다.
|
이벤트 정보를 기반으로 3가지 스타일의 SNS 이미지 생성을 비동기로 요청합니다.
|
||||||
|
|
||||||
## 처리 방식
|
## 처리 방식
|
||||||
- **비동기 처리**: Kafka image-job 토픽에 Job 발행
|
- **비동기 처리**: Kafka `image-generation-job` 토픽에 Job 발행
|
||||||
- **폴링 조회**: jobId로 생성 상태 조회 (GET /api/content/images/{jobId})
|
- **폴링 조회**: jobId로 생성 상태 조회 (GET /api/content/images/jobs/{jobId})
|
||||||
- **캐싱**: 동일한 eventDraftId 재요청 시 캐시 반환 (TTL 7일)
|
- **캐싱**: 동일한 eventDraftId 재요청 시 캐시 반환 (TTL 7일)
|
||||||
|
|
||||||
## 생성 스타일
|
## 생성 스타일
|
||||||
1. **심플 스타일**: 깔끔한 디자인, 텍스트 중심
|
1. **심플 스타일 (SIMPLE)**: 깔끔한 디자인, 텍스트 중심
|
||||||
2. **화려한 스타일**: 눈에 띄는 디자인, 풍부한 색상
|
2. **화려한 스타일 (FANCY)**: 눈에 띄는 디자인, 풍부한 색상
|
||||||
3. **트렌디 스타일**: 최신 트렌드, MZ세대 타겟
|
3. **트렌디 스타일 (TRENDY)**: 최신 트렌드, MZ세대 타겟
|
||||||
|
|
||||||
## Resilience 패턴
|
## Resilience 패턴
|
||||||
- Circuit Breaker (실패율 50% 초과 시 Open)
|
- Circuit Breaker (5분 타임아웃, 실패율 50% 초과 시 Open)
|
||||||
- Fallback (Stable Diffusion → DALL-E → 기본 템플릿)
|
- Fallback (Stable Diffusion → DALL-E → 기본 템플릿)
|
||||||
- Timeout (20초)
|
|
||||||
|
|
||||||
operationId: generateImages
|
operationId: generateImages
|
||||||
|
x-user-story: UFR-CONT-010
|
||||||
|
x-controller: ImageGenerationController.generateImages
|
||||||
requestBody:
|
requestBody:
|
||||||
required: true
|
required: true
|
||||||
content:
|
content:
|
||||||
@ -152,11 +182,11 @@ paths:
|
|||||||
security:
|
security:
|
||||||
- BearerAuth: []
|
- BearerAuth: []
|
||||||
|
|
||||||
/api/content/images/{jobId}:
|
/api/content/images/jobs/{jobId}:
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
- Images
|
- Job Status
|
||||||
summary: 이미지 생성 상태 및 결과 조회
|
summary: 이미지 생성 작업 상태 조회 (폴링)
|
||||||
description: |
|
description: |
|
||||||
jobId로 이미지 생성 상태를 조회합니다.
|
jobId로 이미지 생성 상태를 조회합니다.
|
||||||
|
|
||||||
@ -169,13 +199,15 @@ paths:
|
|||||||
- **PENDING**: 대기 중 (Kafka Queue에서 대기)
|
- **PENDING**: 대기 중 (Kafka Queue에서 대기)
|
||||||
- **PROCESSING**: 생성 중 (AI API 호출 진행)
|
- **PROCESSING**: 생성 중 (AI API 호출 진행)
|
||||||
- **COMPLETED**: 완료 (3가지 이미지 URL 반환)
|
- **COMPLETED**: 완료 (3가지 이미지 URL 반환)
|
||||||
- **FAILED**: 실패 (에러 메시지 포함)
|
- **FAILED**: 실패 (에러 메시지 포함, Fallback 이미지 제공)
|
||||||
|
|
||||||
## 캐싱
|
## 캐싱
|
||||||
- COMPLETED 상태는 Redis 캐싱 (TTL 7일)
|
- COMPLETED 상태는 Redis 캐싱 (TTL 7일)
|
||||||
- 동일한 eventDraftId 재요청 시 즉시 반환
|
- 동일한 eventDraftId 재요청 시 즉시 반환
|
||||||
|
|
||||||
operationId: getImageGenerationStatus
|
operationId: getImageGenerationStatus
|
||||||
|
x-user-story: UFR-CONT-010
|
||||||
|
x-controller: ImageGenerationController.getJobStatus
|
||||||
parameters:
|
parameters:
|
||||||
- name: jobId
|
- name: jobId
|
||||||
in: path
|
in: path
|
||||||
@ -307,6 +339,337 @@ paths:
|
|||||||
security:
|
security:
|
||||||
- BearerAuth: []
|
- 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:
|
components:
|
||||||
securitySchemes:
|
securitySchemes:
|
||||||
BearerAuth:
|
BearerAuth:
|
||||||
@ -316,6 +679,90 @@ components:
|
|||||||
description: JWT 토큰을 Authorization 헤더에 포함 (Bearer {token})
|
description: JWT 토큰을 Authorization 헤더에 포함 (Bearer {token})
|
||||||
|
|
||||||
schemas:
|
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:
|
ImageGenerationRequest:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
@ -546,3 +993,166 @@ components:
|
|||||||
type: integer
|
type: integer
|
||||||
description: 재시도 대기 시간 (초, Rate Limiting 에러에서만)
|
description: 재시도 대기 시간 (초, Rate Limiting 에러에서만)
|
||||||
example: 60
|
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
|
||||||
|
|||||||
@ -2,61 +2,73 @@ openapi: 3.0.3
|
|||||||
info:
|
info:
|
||||||
title: Distribution Service API
|
title: Distribution Service API
|
||||||
description: |
|
description: |
|
||||||
KT AI 기반 소상공인 이벤트 자동 생성 서비스 - 배포 관리 서비스 API
|
KT AI 기반 소상공인 이벤트 자동 생성 서비스의 다중 채널 배포 관리 API
|
||||||
|
|
||||||
**주요 기능:**
|
## 주요 기능
|
||||||
- 다중 채널 배포 관리
|
- 다중 채널 동시 배포 (우리동네TV, 링고비즈, 지니TV, SNS)
|
||||||
- 배포 상태 모니터링
|
- 배포 상태 실시간 모니터링
|
||||||
- 채널별 배포 결과 추적
|
- Circuit Breaker 기반 장애 격리
|
||||||
|
- Retry 패턴 및 Fallback 처리
|
||||||
|
|
||||||
|
## 배포 채널
|
||||||
|
- **우리동네TV**: 영상 콘텐츠 업로드
|
||||||
|
- **링고비즈**: 연결음 업데이트
|
||||||
|
- **지니TV**: 광고 등록
|
||||||
|
- **SNS**: Instagram, Naver Blog, Kakao Channel
|
||||||
|
|
||||||
|
## Resilience 패턴
|
||||||
|
- Circuit Breaker: 채널별 독립적 장애 격리
|
||||||
|
- Retry: 지수 백오프 (1s, 2s, 4s) 최대 3회
|
||||||
|
- Bulkhead: 리소스 격리
|
||||||
|
- Fallback: 실패 채널 스킵 및 알림
|
||||||
|
|
||||||
**지원 배포 채널:**
|
|
||||||
- 우리동네TV
|
|
||||||
- 링고비즈 (연결음)
|
|
||||||
- 지니TV 광고
|
|
||||||
- Instagram
|
|
||||||
- Naver Blog
|
|
||||||
- Kakao Channel
|
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
contact:
|
contact:
|
||||||
name: KT Event Marketing Team
|
name: Backend Development Team
|
||||||
email: support@kt-event-marketing.com
|
email: backend@kt-event-marketing.com
|
||||||
|
|
||||||
servers:
|
servers:
|
||||||
- url: http://localhost:8085
|
- url: http://localhost:8083
|
||||||
description: Local Development Server
|
description: Local Development Server
|
||||||
- url: https://api-dev.kt-event-marketing.com
|
- url: http://distribution-service:8083
|
||||||
description: Development Server
|
description: Docker/Kubernetes Service
|
||||||
- url: https://api.kt-event-marketing.com
|
- url: https://api-dev.kt-event-marketing.com/distribution
|
||||||
description: Production Server
|
description: Development Environment
|
||||||
|
- url: https://api.kt-event-marketing.com/distribution
|
||||||
|
description: Production Environment
|
||||||
|
|
||||||
tags:
|
tags:
|
||||||
- name: Distribution
|
- name: Distribution
|
||||||
description: 다중 채널 배포 관리
|
description: 다중 채널 배포 관리
|
||||||
- name: Status
|
- name: Monitoring
|
||||||
description: 배포 상태 조회 및 모니터링
|
description: 배포 상태 모니터링
|
||||||
|
|
||||||
paths:
|
paths:
|
||||||
/api/distribution/distribute:
|
/api/distribution/distribute:
|
||||||
post:
|
post:
|
||||||
tags:
|
tags:
|
||||||
- Distribution
|
- Distribution
|
||||||
summary: 다중 채널 배포 실행
|
summary: 다중 채널 배포 요청
|
||||||
description: |
|
description: |
|
||||||
선택된 모든 채널에 동시 배포를 실행합니다.
|
이벤트 콘텐츠를 선택된 채널들에 동시 배포합니다.
|
||||||
|
|
||||||
**배포 프로세스:**
|
## 처리 흐름
|
||||||
1. 배포 이력 초기화 (상태: PENDING)
|
1. 배포 요청 검증 (이벤트 ID, 채널 목록, 콘텐츠 데이터)
|
||||||
2. 각 채널별 병렬 배포 처리
|
2. 채널별 병렬 배포 실행 (1분 이내 완료 목표)
|
||||||
3. 배포 결과 집계 및 저장
|
3. Circuit Breaker로 장애 채널 격리
|
||||||
4. Kafka 이벤트 발행 (Analytics Service 구독)
|
4. 실패 시 Retry (지수 백오프: 1s, 2s, 4s)
|
||||||
5. Redis 캐시 저장 (TTL: 1시간)
|
5. Fallback: 실패 채널 스킵 및 알림
|
||||||
|
6. 배포 결과 집계 및 로그 저장
|
||||||
|
7. DistributionCompleted 이벤트 Kafka 발행
|
||||||
|
|
||||||
**Sprint 2 제약사항:**
|
## Resilience 처리
|
||||||
- 외부 API 호출 없음 (Mock 처리)
|
- 각 채널별 독립적인 Circuit Breaker 적용
|
||||||
- 모든 배포 요청은 성공으로 처리
|
- 최대 3회 재시도 (지수 백오프)
|
||||||
- 배포 로그만 DB에 기록
|
- 일부 채널 실패 시에도 성공 채널은 유지
|
||||||
|
- 실패 채널 정보는 응답에 포함
|
||||||
|
|
||||||
**유저스토리:** UFR-DIST-010
|
x-user-story: UFR-DIST-010
|
||||||
|
x-controller: DistributionController
|
||||||
operationId: distributeToChannels
|
operationId: distributeToChannels
|
||||||
requestBody:
|
requestBody:
|
||||||
required: true
|
required: true
|
||||||
@ -165,23 +177,26 @@ paths:
|
|||||||
/api/distribution/{eventId}/status:
|
/api/distribution/{eventId}/status:
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
- Status
|
- Monitoring
|
||||||
summary: 배포 상태 조회
|
summary: 배포 상태 조회
|
||||||
description: |
|
description: |
|
||||||
이벤트의 배포 상태를 조회합니다.
|
특정 이벤트의 배포 상태를 실시간으로 조회합니다.
|
||||||
|
|
||||||
**조회 프로세스:**
|
## 조회 정보
|
||||||
1. Cache-Aside 패턴 적용 (Redis 캐시 우선 조회)
|
- 전체 배포 상태 (진행중, 완료, 부분성공, 실패)
|
||||||
2. 캐시 MISS 시 DB에서 배포 이력 조회
|
- 채널별 배포 상태 및 결과
|
||||||
3. 진행중(IN_PROGRESS) 상태일 때만 외부 API로 실시간 상태 확인
|
- 실패 채널 상세 정보 (오류 유형, 재시도 횟수)
|
||||||
4. Circuit Breaker 패턴 적용 (외부 API 호출 시)
|
- 배포 시작/완료 시간 및 소요 시간
|
||||||
5. 배포 상태 캐싱 (TTL: 1시간)
|
- 외부 채널 ID 및 배포 URL
|
||||||
|
|
||||||
**응답 시간:**
|
## 상태 값
|
||||||
- 캐시 HIT: 0.1초
|
- **IN_PROGRESS**: 배포 진행 중
|
||||||
- 캐시 MISS: 0.5초 ~ 2초
|
- **COMPLETED**: 모든 채널 배포 완료
|
||||||
|
- **PARTIAL_SUCCESS**: 일부 채널 배포 성공
|
||||||
|
- **FAILED**: 모든 채널 배포 실패
|
||||||
|
|
||||||
**유저스토리:** UFR-DIST-020
|
x-user-story: UFR-DIST-020
|
||||||
|
x-controller: DistributionController
|
||||||
operationId: getDistributionStatus
|
operationId: getDistributionStatus
|
||||||
parameters:
|
parameters:
|
||||||
- name: eventId
|
- name: eventId
|
||||||
@ -285,80 +300,6 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/ErrorResponse'
|
$ref: '#/components/schemas/ErrorResponse'
|
||||||
|
|
||||||
/api/distribution/{eventId}/retry:
|
|
||||||
post:
|
|
||||||
tags:
|
|
||||||
- Distribution
|
|
||||||
summary: 실패한 채널 재시도
|
|
||||||
description: |
|
|
||||||
실패한 채널에 대해 배포를 재시도합니다.
|
|
||||||
|
|
||||||
**재시도 프로세스:**
|
|
||||||
1. 실패한 채널 목록 검증
|
|
||||||
2. 새로운 배포 시도 로그 생성
|
|
||||||
3. Circuit Breaker 및 Retry 로직 적용
|
|
||||||
4. 캐시 무효화
|
|
||||||
5. 재시도 결과 반환
|
|
||||||
|
|
||||||
**재시도 제한:**
|
|
||||||
- 최대 재시도 횟수: 3회
|
|
||||||
- Circuit Breaker가 OPEN 상태일 경우 30초 대기 후 시도
|
|
||||||
operationId: retryDistribution
|
|
||||||
parameters:
|
|
||||||
- name: eventId
|
|
||||||
in: path
|
|
||||||
required: true
|
|
||||||
description: 이벤트 ID
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
example: "evt-12345"
|
|
||||||
requestBody:
|
|
||||||
required: true
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/RetryRequest'
|
|
||||||
examples:
|
|
||||||
retryFailedChannels:
|
|
||||||
summary: 실패한 채널 재시도
|
|
||||||
value:
|
|
||||||
channels:
|
|
||||||
- "INSTAGRAM"
|
|
||||||
- "KAKAO_CHANNEL"
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: 재시도 완료
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/RetryResponse'
|
|
||||||
examples:
|
|
||||||
success:
|
|
||||||
summary: 재시도 성공
|
|
||||||
value:
|
|
||||||
eventId: "evt-12345"
|
|
||||||
retryStatus: "COMPLETED"
|
|
||||||
retriedAt: "2025-11-01T09:05:00Z"
|
|
||||||
results:
|
|
||||||
- channel: "INSTAGRAM"
|
|
||||||
status: "SUCCESS"
|
|
||||||
postUrl: "https://instagram.com/p/retry-post-id"
|
|
||||||
- channel: "KAKAO_CHANNEL"
|
|
||||||
status: "SUCCESS"
|
|
||||||
messageId: "kakao-retry-12345"
|
|
||||||
'400':
|
|
||||||
description: 잘못된 요청
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/ErrorResponse'
|
|
||||||
'404':
|
|
||||||
description: 배포 이력을 찾을 수 없음
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/ErrorResponse'
|
|
||||||
|
|
||||||
components:
|
components:
|
||||||
schemas:
|
schemas:
|
||||||
DistributionRequest:
|
DistributionRequest:
|
||||||
@ -682,58 +623,6 @@ components:
|
|||||||
description: 마지막 재시도 시각
|
description: 마지막 재시도 시각
|
||||||
example: "2025-11-01T08:59:30Z"
|
example: "2025-11-01T08:59:30Z"
|
||||||
|
|
||||||
RetryRequest:
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- channels
|
|
||||||
properties:
|
|
||||||
channels:
|
|
||||||
type: array
|
|
||||||
description: 재시도할 채널 목록
|
|
||||||
minItems: 1
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
enum:
|
|
||||||
- WOORIDONGNE_TV
|
|
||||||
- RINGO_BIZ
|
|
||||||
- GENIE_TV
|
|
||||||
- INSTAGRAM
|
|
||||||
- NAVER_BLOG
|
|
||||||
- KAKAO_CHANNEL
|
|
||||||
example:
|
|
||||||
- "INSTAGRAM"
|
|
||||||
- "KAKAO_CHANNEL"
|
|
||||||
|
|
||||||
RetryResponse:
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- eventId
|
|
||||||
- retryStatus
|
|
||||||
- results
|
|
||||||
properties:
|
|
||||||
eventId:
|
|
||||||
type: string
|
|
||||||
description: 이벤트 ID
|
|
||||||
example: "evt-12345"
|
|
||||||
retryStatus:
|
|
||||||
type: string
|
|
||||||
description: 재시도 전체 상태
|
|
||||||
enum:
|
|
||||||
- COMPLETED
|
|
||||||
- PARTIAL_FAILURE
|
|
||||||
- FAILED
|
|
||||||
example: "COMPLETED"
|
|
||||||
retriedAt:
|
|
||||||
type: string
|
|
||||||
format: date-time
|
|
||||||
description: 재시도 시각
|
|
||||||
example: "2025-11-01T09:05:00Z"
|
|
||||||
results:
|
|
||||||
type: array
|
|
||||||
description: 채널별 재시도 결과
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/ChannelResult'
|
|
||||||
|
|
||||||
ErrorResponse:
|
ErrorResponse:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -487,6 +487,101 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/ErrorResponse'
|
$ref: '#/components/schemas/ErrorResponse'
|
||||||
|
|
||||||
|
/api/users/{userId}/store:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- Profile
|
||||||
|
summary: 매장정보 조회 (서비스 연동용)
|
||||||
|
description: |
|
||||||
|
특정 사용자의 매장정보를 조회하는 API (내부 서비스 연동용)
|
||||||
|
|
||||||
|
**사용 목적:**
|
||||||
|
- Event Service에서 이벤트 생성 시 매장정보 조회
|
||||||
|
- Content Service에서 매장정보 기반 콘텐츠 생성
|
||||||
|
- Service-to-Service 통신용 내부 API
|
||||||
|
|
||||||
|
**주의사항:**
|
||||||
|
- Internal API로 외부 노출 금지
|
||||||
|
- API Gateway에서 인증된 서비스만 접근 허용
|
||||||
|
- 매장정보는 Redis 캐시 우선 조회 (TTL 30분)
|
||||||
|
operationId: getStoreByUserId
|
||||||
|
x-user-story: Service Integration
|
||||||
|
x-controller: UserController
|
||||||
|
security:
|
||||||
|
- BearerAuth: []
|
||||||
|
parameters:
|
||||||
|
- name: userId
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
description: 사용자 ID
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
example: 123
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 매장정보 조회 성공
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/StoreDetailResponse'
|
||||||
|
examples:
|
||||||
|
success:
|
||||||
|
summary: 매장정보 조회 성공 응답
|
||||||
|
value:
|
||||||
|
userId: 123
|
||||||
|
storeId: 456
|
||||||
|
storeName: 맛있는집
|
||||||
|
industry: 음식점
|
||||||
|
address: 서울시 강남구 테헤란로 123
|
||||||
|
businessHours: "월-금 11:00-22:00, 토-일 12:00-21:00"
|
||||||
|
businessNumber: "1234567890"
|
||||||
|
'401':
|
||||||
|
description: 인증 실패
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ErrorResponse'
|
||||||
|
examples:
|
||||||
|
unauthorized:
|
||||||
|
summary: 인증 실패
|
||||||
|
value:
|
||||||
|
code: AUTH_002
|
||||||
|
error: 유효하지 않은 토큰입니다
|
||||||
|
timestamp: 2025-10-22T10:30:00Z
|
||||||
|
'403':
|
||||||
|
description: 권한 없음 (내부 서비스만 접근 가능)
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ErrorResponse'
|
||||||
|
examples:
|
||||||
|
forbidden:
|
||||||
|
summary: 권한 없음
|
||||||
|
value:
|
||||||
|
code: AUTH_003
|
||||||
|
error: 이 API는 내부 서비스만 접근 가능합니다
|
||||||
|
timestamp: 2025-10-22T10:30:00Z
|
||||||
|
'404':
|
||||||
|
description: 사용자 또는 매장을 찾을 수 없음
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ErrorResponse'
|
||||||
|
examples:
|
||||||
|
notFound:
|
||||||
|
summary: 사용자 또는 매장 없음
|
||||||
|
value:
|
||||||
|
code: USER_003
|
||||||
|
error: 사용자 또는 매장을 찾을 수 없습니다
|
||||||
|
timestamp: 2025-10-22T10:30:00Z
|
||||||
|
'500':
|
||||||
|
description: 서버 오류
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ErrorResponse'
|
||||||
|
|
||||||
components:
|
components:
|
||||||
securitySchemes:
|
securitySchemes:
|
||||||
BearerAuth:
|
BearerAuth:
|
||||||
@ -844,6 +939,46 @@ components:
|
|||||||
description: 응답 메시지
|
description: 응답 메시지
|
||||||
example: 비밀번호가 성공적으로 변경되었습니다
|
example: 비밀번호가 성공적으로 변경되었습니다
|
||||||
|
|
||||||
|
StoreDetailResponse:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- userId
|
||||||
|
- storeId
|
||||||
|
- storeName
|
||||||
|
- industry
|
||||||
|
- address
|
||||||
|
properties:
|
||||||
|
userId:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
description: 사용자 ID
|
||||||
|
example: 123
|
||||||
|
storeId:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
description: 매장 ID
|
||||||
|
example: 456
|
||||||
|
storeName:
|
||||||
|
type: string
|
||||||
|
description: 매장명
|
||||||
|
example: 맛있는집
|
||||||
|
industry:
|
||||||
|
type: string
|
||||||
|
description: 업종
|
||||||
|
example: 음식점
|
||||||
|
address:
|
||||||
|
type: string
|
||||||
|
description: 매장 주소
|
||||||
|
example: 서울시 강남구 테헤란로 123
|
||||||
|
businessHours:
|
||||||
|
type: string
|
||||||
|
description: 영업시간
|
||||||
|
example: "월-금 11:00-22:00, 토-일 12:00-21:00"
|
||||||
|
businessNumber:
|
||||||
|
type: string
|
||||||
|
description: 사업자번호 (10자리)
|
||||||
|
example: "1234567890"
|
||||||
|
|
||||||
ErrorResponse:
|
ErrorResponse:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
@ -863,6 +998,7 @@ components:
|
|||||||
- USER_005 # 동시성 충돌
|
- USER_005 # 동시성 충돌
|
||||||
- AUTH_001 # 인증 실패
|
- AUTH_001 # 인증 실패
|
||||||
- AUTH_002 # 유효하지 않은 토큰
|
- AUTH_002 # 유효하지 않은 토큰
|
||||||
|
- AUTH_003 # 권한 없음 (내부 서비스만 접근)
|
||||||
- VALIDATION_ERROR # 입력 검증 오류
|
- VALIDATION_ERROR # 입력 검증 오류
|
||||||
error:
|
error:
|
||||||
type: string
|
type: string
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user