mirror of
https://github.com/ktds-dg0501/kt-event-marketing.git
synced 2025-12-06 10:06:24 +00:00
- 공통 OpenAPI 3.0.3 컨벤션 문서 작성 (design/backend/api/API_CONVENTION.md) - 7개 서비스 API 명세서 표준화 완료: * User Service (8081) * Event Service (8080) * Content Service (8082) * AI Service (8083) * Participation Service (8084) * Distribution Service (8085) * Analytics Service (8086) 주요 변경사항: - API 경로에서 /api prefix 제거 (/api/users → /users) - 서버 URL 패턴 통일 (Local → Dev → Prod) - 연락처 정보 표준화 (Digital Garage Team) - ErrorResponse 스키마 통일 (error → message, details 추가) - securitySchemes 이름 통일 (BearerAuth) - 포트 번호 명확히 할당 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
652 lines
21 KiB
YAML
652 lines
21 KiB
YAML
openapi: 3.0.3
|
|
info:
|
|
title: Distribution Service API
|
|
description: |
|
|
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: 실패 채널 스킵 및 알림
|
|
|
|
version: 1.0.0
|
|
contact:
|
|
name: Digital Garage Team
|
|
email: support@kt-event-marketing.com
|
|
|
|
servers:
|
|
- url: http://localhost:8085
|
|
description: Local Development Server
|
|
- url: https://dev-api.kt-event-marketing.com/distribution/v1
|
|
description: Development Server
|
|
- url: https://api.kt-event-marketing.com/distribution/v1
|
|
description: Production Server
|
|
|
|
tags:
|
|
- name: Distribution
|
|
description: 다중 채널 배포 관리
|
|
- name: Monitoring
|
|
description: 배포 상태 모니터링
|
|
|
|
paths:
|
|
/distribution/distribute:
|
|
post:
|
|
tags:
|
|
- Distribution
|
|
summary: 다중 채널 배포 요청
|
|
description: |
|
|
이벤트 콘텐츠를 선택된 채널들에 동시 배포합니다.
|
|
|
|
## 처리 흐름
|
|
1. 배포 요청 검증 (이벤트 ID, 채널 목록, 콘텐츠 데이터)
|
|
2. 채널별 병렬 배포 실행 (1분 이내 완료 목표)
|
|
3. Circuit Breaker로 장애 채널 격리
|
|
4. 실패 시 Retry (지수 백오프: 1s, 2s, 4s)
|
|
5. Fallback: 실패 채널 스킵 및 알림
|
|
6. 배포 결과 집계 및 로그 저장
|
|
7. DistributionCompleted 이벤트 Kafka 발행
|
|
|
|
## Resilience 처리
|
|
- 각 채널별 독립적인 Circuit Breaker 적용
|
|
- 최대 3회 재시도 (지수 백오프)
|
|
- 일부 채널 실패 시에도 성공 채널은 유지
|
|
- 실패 채널 정보는 응답에 포함
|
|
|
|
x-user-story: UFR-DIST-010
|
|
x-controller: DistributionController
|
|
operationId: distributeToChannels
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/DistributionRequest'
|
|
examples:
|
|
multiChannel:
|
|
summary: 다중 채널 배포 예시
|
|
value:
|
|
eventId: "evt-12345"
|
|
channels:
|
|
- type: "WOORIDONGNE_TV"
|
|
config:
|
|
radius: "1km"
|
|
timeSlots:
|
|
- "weekday_evening"
|
|
- "weekend_lunch"
|
|
- type: "INSTAGRAM"
|
|
config:
|
|
scheduledTime: "2025-11-01T10:00:00Z"
|
|
- type: "NAVER_BLOG"
|
|
config:
|
|
scheduledTime: "2025-11-01T10:30:00Z"
|
|
contentUrls:
|
|
instagram: "https://cdn.example.com/images/event-instagram.jpg"
|
|
naverBlog: "https://cdn.example.com/images/event-naver.jpg"
|
|
kakaoChannel: "https://cdn.example.com/images/event-kakao.jpg"
|
|
responses:
|
|
'200':
|
|
description: 배포 완료
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/DistributionResponse'
|
|
examples:
|
|
allSuccess:
|
|
summary: 모든 채널 배포 성공
|
|
value:
|
|
distributionId: "dist-12345"
|
|
eventId: "evt-12345"
|
|
status: "COMPLETED"
|
|
completedAt: "2025-11-01T09:00:00Z"
|
|
results:
|
|
- channel: "WOORIDONGNE_TV"
|
|
status: "SUCCESS"
|
|
distributionId: "wtv-uuid-12345"
|
|
estimatedViews: 1000
|
|
message: "배포 완료"
|
|
- channel: "INSTAGRAM"
|
|
status: "SUCCESS"
|
|
postUrl: "https://instagram.com/p/generated-post-id"
|
|
postId: "ig-post-12345"
|
|
message: "게시 완료"
|
|
- channel: "NAVER_BLOG"
|
|
status: "SUCCESS"
|
|
postUrl: "https://blog.naver.com/store123/generated-post"
|
|
message: "게시 완료"
|
|
'400':
|
|
description: 잘못된 요청
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
examples:
|
|
invalidEventId:
|
|
summary: 유효하지 않은 이벤트 ID
|
|
value:
|
|
error: "BAD_REQUEST"
|
|
message: "유효하지 않은 이벤트 ID입니다"
|
|
timestamp: "2025-11-01T09:00:00Z"
|
|
noChannels:
|
|
summary: 선택된 채널 없음
|
|
value:
|
|
error: "BAD_REQUEST"
|
|
message: "최소 1개 이상의 채널을 선택해야 합니다"
|
|
timestamp: "2025-11-01T09:00:00Z"
|
|
'404':
|
|
description: 이벤트를 찾을 수 없음
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
examples:
|
|
eventNotFound:
|
|
summary: 존재하지 않는 이벤트
|
|
value:
|
|
error: "NOT_FOUND"
|
|
message: "이벤트를 찾을 수 없습니다: evt-12345"
|
|
timestamp: "2025-11-01T09:00:00Z"
|
|
'500':
|
|
description: 서버 내부 오류
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
examples:
|
|
internalError:
|
|
summary: 서버 오류
|
|
value:
|
|
error: "INTERNAL_SERVER_ERROR"
|
|
message: "배포 처리 중 오류가 발생했습니다"
|
|
timestamp: "2025-11-01T09:00:00Z"
|
|
|
|
/distribution/{eventId}/status:
|
|
get:
|
|
tags:
|
|
- Monitoring
|
|
summary: 배포 상태 조회
|
|
description: |
|
|
특정 이벤트의 배포 상태를 실시간으로 조회합니다.
|
|
|
|
## 조회 정보
|
|
- 전체 배포 상태 (진행중, 완료, 부분성공, 실패)
|
|
- 채널별 배포 상태 및 결과
|
|
- 실패 채널 상세 정보 (오류 유형, 재시도 횟수)
|
|
- 배포 시작/완료 시간 및 소요 시간
|
|
- 외부 채널 ID 및 배포 URL
|
|
|
|
## 상태 값
|
|
- **IN_PROGRESS**: 배포 진행 중
|
|
- **COMPLETED**: 모든 채널 배포 완료
|
|
- **PARTIAL_SUCCESS**: 일부 채널 배포 성공
|
|
- **FAILED**: 모든 채널 배포 실패
|
|
|
|
x-user-story: UFR-DIST-020
|
|
x-controller: DistributionController
|
|
operationId: getDistributionStatus
|
|
parameters:
|
|
- name: eventId
|
|
in: path
|
|
required: true
|
|
description: 이벤트 ID
|
|
schema:
|
|
type: string
|
|
example: "evt-12345"
|
|
responses:
|
|
'200':
|
|
description: 배포 상태 조회 성공
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/DistributionStatusResponse'
|
|
examples:
|
|
completed:
|
|
summary: 배포 완료 상태
|
|
value:
|
|
eventId: "evt-12345"
|
|
overallStatus: "COMPLETED"
|
|
completedAt: "2025-11-01T09:00:00Z"
|
|
channels:
|
|
- channel: "WOORIDONGNE_TV"
|
|
status: "COMPLETED"
|
|
distributionId: "wtv-uuid-12345"
|
|
estimatedViews: 1500
|
|
completedAt: "2025-11-01T09:00:00Z"
|
|
- channel: "RINGO_BIZ"
|
|
status: "COMPLETED"
|
|
updateTimestamp: "2025-11-01T09:00:00Z"
|
|
- channel: "GENIE_TV"
|
|
status: "COMPLETED"
|
|
adId: "gtv-uuid-12345"
|
|
impressionSchedule:
|
|
- "2025-11-01 18:00-20:00"
|
|
- "2025-11-02 12:00-14:00"
|
|
- channel: "INSTAGRAM"
|
|
status: "COMPLETED"
|
|
postUrl: "https://instagram.com/p/generated-post-id"
|
|
postId: "ig-post-12345"
|
|
- channel: "NAVER_BLOG"
|
|
status: "COMPLETED"
|
|
postUrl: "https://blog.naver.com/store123/generated-post"
|
|
- channel: "KAKAO_CHANNEL"
|
|
status: "COMPLETED"
|
|
messageId: "kakao-msg-12345"
|
|
inProgress:
|
|
summary: 배포 진행중 상태
|
|
value:
|
|
eventId: "evt-12345"
|
|
overallStatus: "IN_PROGRESS"
|
|
startedAt: "2025-11-01T08:58:00Z"
|
|
channels:
|
|
- channel: "WOORIDONGNE_TV"
|
|
status: "COMPLETED"
|
|
distributionId: "wtv-uuid-12345"
|
|
estimatedViews: 1500
|
|
- channel: "INSTAGRAM"
|
|
status: "IN_PROGRESS"
|
|
progress: 50
|
|
- channel: "NAVER_BLOG"
|
|
status: "PENDING"
|
|
partialFailure:
|
|
summary: 일부 채널 실패 상태
|
|
value:
|
|
eventId: "evt-12345"
|
|
overallStatus: "PARTIAL_FAILURE"
|
|
completedAt: "2025-11-01T09:00:00Z"
|
|
channels:
|
|
- channel: "WOORIDONGNE_TV"
|
|
status: "COMPLETED"
|
|
distributionId: "wtv-uuid-12345"
|
|
estimatedViews: 1500
|
|
- channel: "INSTAGRAM"
|
|
status: "FAILED"
|
|
errorMessage: "Instagram API 타임아웃"
|
|
retries: 3
|
|
lastRetryAt: "2025-11-01T08:59:30Z"
|
|
- channel: "NAVER_BLOG"
|
|
status: "COMPLETED"
|
|
postUrl: "https://blog.naver.com/store123/generated-post"
|
|
'404':
|
|
description: 배포 이력을 찾을 수 없음
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
examples:
|
|
notFound:
|
|
summary: 배포 이력 없음
|
|
value:
|
|
error: "NOT_FOUND"
|
|
message: "배포 이력을 찾을 수 없습니다: evt-12345"
|
|
timestamp: "2025-11-01T09:00:00Z"
|
|
'500':
|
|
description: 서버 내부 오류
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
|
|
components:
|
|
schemas:
|
|
DistributionRequest:
|
|
type: object
|
|
required:
|
|
- eventId
|
|
- channels
|
|
- contentUrls
|
|
properties:
|
|
eventId:
|
|
type: string
|
|
description: 이벤트 ID
|
|
example: "evt-12345"
|
|
channels:
|
|
type: array
|
|
description: 배포할 채널 목록
|
|
minItems: 1
|
|
items:
|
|
$ref: '#/components/schemas/ChannelConfig'
|
|
contentUrls:
|
|
type: object
|
|
description: 플랫폼별 콘텐츠 URL
|
|
properties:
|
|
wooridongneTV:
|
|
type: string
|
|
description: 우리동네TV 영상 URL (15초)
|
|
example: "https://cdn.example.com/videos/event-15s.mp4"
|
|
ringoBiz:
|
|
type: string
|
|
description: 링고비즈 연결음 파일 URL
|
|
example: "https://cdn.example.com/audio/ringtone.mp3"
|
|
genieTV:
|
|
type: string
|
|
description: 지니TV 광고 영상 URL
|
|
example: "https://cdn.example.com/videos/event-ad.mp4"
|
|
instagram:
|
|
type: string
|
|
description: Instagram 이미지 URL (1080x1080)
|
|
example: "https://cdn.example.com/images/event-instagram.jpg"
|
|
naverBlog:
|
|
type: string
|
|
description: Naver Blog 이미지 URL (800x600)
|
|
example: "https://cdn.example.com/images/event-naver.jpg"
|
|
kakaoChannel:
|
|
type: string
|
|
description: Kakao Channel 이미지 URL (800x800)
|
|
example: "https://cdn.example.com/images/event-kakao.jpg"
|
|
|
|
ChannelConfig:
|
|
type: object
|
|
required:
|
|
- type
|
|
properties:
|
|
type:
|
|
type: string
|
|
description: 채널 타입
|
|
enum:
|
|
- WOORIDONGNE_TV
|
|
- RINGO_BIZ
|
|
- GENIE_TV
|
|
- INSTAGRAM
|
|
- NAVER_BLOG
|
|
- KAKAO_CHANNEL
|
|
example: "INSTAGRAM"
|
|
config:
|
|
type: object
|
|
description: 채널별 설정 (채널에 따라 다름)
|
|
additionalProperties: true
|
|
example:
|
|
scheduledTime: "2025-11-01T10:00:00Z"
|
|
caption: "이벤트 안내"
|
|
hashtags:
|
|
- "이벤트"
|
|
- "할인"
|
|
|
|
DistributionResponse:
|
|
type: object
|
|
required:
|
|
- distributionId
|
|
- eventId
|
|
- status
|
|
- results
|
|
properties:
|
|
distributionId:
|
|
type: string
|
|
description: 배포 ID
|
|
example: "dist-12345"
|
|
eventId:
|
|
type: string
|
|
description: 이벤트 ID
|
|
example: "evt-12345"
|
|
status:
|
|
type: string
|
|
description: 전체 배포 상태
|
|
enum:
|
|
- PENDING
|
|
- IN_PROGRESS
|
|
- COMPLETED
|
|
- PARTIAL_FAILURE
|
|
- FAILED
|
|
example: "COMPLETED"
|
|
startedAt:
|
|
type: string
|
|
format: date-time
|
|
description: 배포 시작 시각
|
|
example: "2025-11-01T08:59:00Z"
|
|
completedAt:
|
|
type: string
|
|
format: date-time
|
|
description: 배포 완료 시각
|
|
example: "2025-11-01T09:00:00Z"
|
|
results:
|
|
type: array
|
|
description: 채널별 배포 결과
|
|
items:
|
|
$ref: '#/components/schemas/ChannelResult'
|
|
|
|
ChannelResult:
|
|
type: object
|
|
required:
|
|
- channel
|
|
- status
|
|
properties:
|
|
channel:
|
|
type: string
|
|
description: 채널 타입
|
|
enum:
|
|
- WOORIDONGNE_TV
|
|
- RINGO_BIZ
|
|
- GENIE_TV
|
|
- INSTAGRAM
|
|
- NAVER_BLOG
|
|
- KAKAO_CHANNEL
|
|
example: "INSTAGRAM"
|
|
status:
|
|
type: string
|
|
description: 채널별 배포 상태
|
|
enum:
|
|
- PENDING
|
|
- IN_PROGRESS
|
|
- SUCCESS
|
|
- FAILED
|
|
example: "SUCCESS"
|
|
distributionId:
|
|
type: string
|
|
description: 채널별 배포 ID (우리동네TV, 지니TV)
|
|
example: "wtv-uuid-12345"
|
|
estimatedViews:
|
|
type: integer
|
|
description: 예상 노출 수 (우리동네TV, 지니TV)
|
|
example: 1500
|
|
updateTimestamp:
|
|
type: string
|
|
format: date-time
|
|
description: 업데이트 완료 시각 (링고비즈)
|
|
example: "2025-11-01T09:00:00Z"
|
|
adId:
|
|
type: string
|
|
description: 광고 ID (지니TV)
|
|
example: "gtv-uuid-12345"
|
|
impressionSchedule:
|
|
type: array
|
|
description: 노출 스케줄 (지니TV)
|
|
items:
|
|
type: string
|
|
example:
|
|
- "2025-11-01 18:00-20:00"
|
|
- "2025-11-02 12:00-14:00"
|
|
postUrl:
|
|
type: string
|
|
description: 게시물 URL (Instagram, Naver Blog)
|
|
example: "https://instagram.com/p/generated-post-id"
|
|
postId:
|
|
type: string
|
|
description: 게시물 ID (Instagram)
|
|
example: "ig-post-12345"
|
|
messageId:
|
|
type: string
|
|
description: 메시지 ID (Kakao Channel)
|
|
example: "kakao-msg-12345"
|
|
message:
|
|
type: string
|
|
description: 결과 메시지
|
|
example: "배포 완료"
|
|
errorMessage:
|
|
type: string
|
|
description: 오류 메시지 (실패 시)
|
|
example: "Instagram API 타임아웃"
|
|
retries:
|
|
type: integer
|
|
description: 재시도 횟수
|
|
example: 0
|
|
lastRetryAt:
|
|
type: string
|
|
format: date-time
|
|
description: 마지막 재시도 시각
|
|
example: "2025-11-01T08:59:30Z"
|
|
|
|
DistributionStatusResponse:
|
|
type: object
|
|
required:
|
|
- eventId
|
|
- overallStatus
|
|
- channels
|
|
properties:
|
|
eventId:
|
|
type: string
|
|
description: 이벤트 ID
|
|
example: "evt-12345"
|
|
overallStatus:
|
|
type: string
|
|
description: 전체 배포 상태
|
|
enum:
|
|
- PENDING
|
|
- IN_PROGRESS
|
|
- COMPLETED
|
|
- PARTIAL_FAILURE
|
|
- FAILED
|
|
- NOT_FOUND
|
|
example: "COMPLETED"
|
|
startedAt:
|
|
type: string
|
|
format: date-time
|
|
description: 배포 시작 시각
|
|
example: "2025-11-01T08:59:00Z"
|
|
completedAt:
|
|
type: string
|
|
format: date-time
|
|
description: 배포 완료 시각
|
|
example: "2025-11-01T09:00:00Z"
|
|
channels:
|
|
type: array
|
|
description: 채널별 배포 상태
|
|
items:
|
|
$ref: '#/components/schemas/ChannelStatus'
|
|
|
|
ChannelStatus:
|
|
type: object
|
|
required:
|
|
- channel
|
|
- status
|
|
properties:
|
|
channel:
|
|
type: string
|
|
description: 채널 타입
|
|
enum:
|
|
- WOORIDONGNE_TV
|
|
- RINGO_BIZ
|
|
- GENIE_TV
|
|
- INSTAGRAM
|
|
- NAVER_BLOG
|
|
- KAKAO_CHANNEL
|
|
example: "INSTAGRAM"
|
|
status:
|
|
type: string
|
|
description: 채널별 배포 상태
|
|
enum:
|
|
- PENDING
|
|
- IN_PROGRESS
|
|
- COMPLETED
|
|
- FAILED
|
|
example: "COMPLETED"
|
|
progress:
|
|
type: integer
|
|
description: 진행률 (0-100, IN_PROGRESS 상태일 때)
|
|
minimum: 0
|
|
maximum: 100
|
|
example: 75
|
|
distributionId:
|
|
type: string
|
|
description: 채널별 배포 ID
|
|
example: "wtv-uuid-12345"
|
|
estimatedViews:
|
|
type: integer
|
|
description: 예상 노출 수
|
|
example: 1500
|
|
updateTimestamp:
|
|
type: string
|
|
format: date-time
|
|
description: 업데이트 완료 시각
|
|
example: "2025-11-01T09:00:00Z"
|
|
adId:
|
|
type: string
|
|
description: 광고 ID
|
|
example: "gtv-uuid-12345"
|
|
impressionSchedule:
|
|
type: array
|
|
description: 노출 스케줄
|
|
items:
|
|
type: string
|
|
example:
|
|
- "2025-11-01 18:00-20:00"
|
|
postUrl:
|
|
type: string
|
|
description: 게시물 URL
|
|
example: "https://instagram.com/p/generated-post-id"
|
|
postId:
|
|
type: string
|
|
description: 게시물 ID
|
|
example: "ig-post-12345"
|
|
messageId:
|
|
type: string
|
|
description: 메시지 ID
|
|
example: "kakao-msg-12345"
|
|
completedAt:
|
|
type: string
|
|
format: date-time
|
|
description: 완료 시각
|
|
example: "2025-11-01T09:00:00Z"
|
|
errorMessage:
|
|
type: string
|
|
description: 오류 메시지
|
|
example: "Instagram API 타임아웃"
|
|
retries:
|
|
type: integer
|
|
description: 재시도 횟수
|
|
example: 3
|
|
lastRetryAt:
|
|
type: string
|
|
format: date-time
|
|
description: 마지막 재시도 시각
|
|
example: "2025-11-01T08:59:30Z"
|
|
|
|
ErrorResponse:
|
|
type: object
|
|
required:
|
|
- error
|
|
- message
|
|
- timestamp
|
|
properties:
|
|
error:
|
|
type: string
|
|
description: 오류 코드
|
|
enum:
|
|
- BAD_REQUEST
|
|
- NOT_FOUND
|
|
- INTERNAL_SERVER_ERROR
|
|
example: "BAD_REQUEST"
|
|
message:
|
|
type: string
|
|
description: 오류 메시지
|
|
example: "유효하지 않은 이벤트 ID입니다"
|
|
timestamp:
|
|
type: string
|
|
format: date-time
|
|
description: 오류 발생 시각
|
|
example: "2025-11-01T09:00:00Z"
|
|
details:
|
|
type: object
|
|
description: 추가 오류 정보 (선택 사항)
|
|
additionalProperties: true
|