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