openapi: 3.0.3 info: title: Distribution Service API description: | KT AI 기반 소상공인 이벤트 자동 생성 서비스의 다중 채널 배포 관리 API ## 주요 기능 - 다중 채널 동시 배포 (우리동네TV, 링고비즈, 지니TV, SNS) - 배포 상태 실시간 모니터링 - Circuit Breaker 기반 장애 격리 - Retry 패턴 및 Fallback 처리 ## 배포 채널 - **우리동네TV** (URIDONGNETV): 영상 콘텐츠 업로드 - **링고비즈** (RINGOBIZ): 연결음 업데이트 - **지니TV** (GINITV): 광고 등록 - **SNS**: Instagram (INSTAGRAM), Naver Blog (NAVER), Kakao Channel (KAKAO) ## 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" title: "신규 고객 환영 이벤트" description: "신규 고객님을 위한 특별 할인 이벤트" imageUrl: "https://cdn.example.com/images/event-main.jpg" channels: - "URIDONGNETV" - "INSTAGRAM" - "NAVER" channelSettings: URIDONGNETV: radius: "1km" timeSlot: "evening" INSTAGRAM: scheduledTime: "2025-11-01T10:00:00" NAVER: scheduledTime: "2025-11-01T10:30:00" responses: '200': description: 배포 완료 content: application/json: schema: $ref: '#/components/schemas/DistributionResponse' examples: allSuccess: summary: 모든 채널 배포 성공 value: eventId: "evt-12345" success: true channelResults: - channel: "URIDONGNETV" success: true distributionId: "wtv-uuid-12345" estimatedReach: 1000 executionTimeMs: 234 - channel: "INSTAGRAM" success: true distributionId: "ig-uuid-12345" estimatedReach: 500 executionTimeMs: 456 - channel: "NAVER" success: true distributionId: "naver-uuid-12345" estimatedReach: 300 executionTimeMs: 123 successCount: 3 failureCount: 0 completedAt: "2025-11-01T09:00:00" totalExecutionTimeMs: 1234 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" startedAt: "2025-11-01T08:58:00" completedAt: "2025-11-01T09:00:00" channels: - channel: "URIDONGNETV" status: "COMPLETED" distributionId: "wtv-uuid-12345" estimatedViews: 1500 completedAt: "2025-11-01T09:00:00" - channel: "RINGOBIZ" status: "COMPLETED" updateTimestamp: "2025-11-01T09:00:00" completedAt: "2025-11-01T09:00:00" - channel: "GINITV" status: "COMPLETED" adId: "gtv-uuid-12345" impressionSchedule: - "2025-11-01 18:00-20:00" - "2025-11-02 12:00-14:00" completedAt: "2025-11-01T09:00:00" - channel: "INSTAGRAM" status: "COMPLETED" postUrl: "https://instagram.com/p/generated-post-id" postId: "ig-post-12345" completedAt: "2025-11-01T09:00:00" - channel: "NAVER" status: "COMPLETED" postUrl: "https://blog.naver.com/store123/generated-post" completedAt: "2025-11-01T09:00:00" - channel: "KAKAO" status: "COMPLETED" messageId: "kakao-msg-12345" completedAt: "2025-11-01T09:00:00" inProgress: summary: 배포 진행중 상태 value: eventId: "evt-12345" overallStatus: "IN_PROGRESS" startedAt: "2025-11-01T08:58:00" channels: - channel: "URIDONGNETV" status: "COMPLETED" distributionId: "wtv-uuid-12345" estimatedViews: 1500 completedAt: "2025-11-01T08:59:00" - channel: "INSTAGRAM" status: "IN_PROGRESS" progress: 50 - channel: "NAVER" status: "PENDING" partialFailure: summary: 일부 채널 실패 상태 value: eventId: "evt-12345" overallStatus: "PARTIAL_FAILURE" startedAt: "2025-11-01T08:58:00" completedAt: "2025-11-01T09:00:00" channels: - channel: "URIDONGNETV" status: "COMPLETED" distributionId: "wtv-uuid-12345" estimatedViews: 1500 completedAt: "2025-11-01T08:59:00" - channel: "INSTAGRAM" status: "FAILED" errorMessage: "Instagram API 타임아웃" retries: 3 lastRetryAt: "2025-11-01T08:59:30" - channel: "NAVER" status: "COMPLETED" postUrl: "https://blog.naver.com/store123/generated-post" completedAt: "2025-11-01T09:00:00" '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 properties: eventId: type: string description: 이벤트 ID example: "evt-12345" title: type: string description: 이벤트 제목 example: "신규 고객 환영 이벤트" description: type: string description: 이벤트 설명 example: "신규 고객님을 위한 특별 할인 이벤트" imageUrl: type: string description: 이미지 URL (CDN) example: "https://cdn.example.com/images/event-main.jpg" channels: type: array description: 배포할 채널 목록 minItems: 1 items: type: string enum: - URIDONGNETV - RINGOBIZ - GINITV - INSTAGRAM - NAVER - KAKAO example: ["URIDONGNETV", "INSTAGRAM", "NAVER"] channelSettings: type: object description: 채널별 추가 설정 (Optional) additionalProperties: type: object additionalProperties: true example: URIDONGNETV: radius: "1km" timeSlot: "evening" INSTAGRAM: scheduledTime: "2025-11-01T10:00:00" DistributionResponse: type: object required: - eventId - success - channelResults - successCount - failureCount properties: eventId: type: string description: 이벤트 ID example: "evt-12345" success: type: boolean description: 배포 성공 여부 (모든 채널 또는 일부 채널 성공) example: true channelResults: type: array description: 채널별 배포 결과 items: $ref: '#/components/schemas/ChannelDistributionResult' successCount: type: integer description: 성공한 채널 수 example: 3 failureCount: type: integer description: 실패한 채널 수 example: 0 completedAt: type: string format: date-time description: 배포 완료 시각 example: "2025-11-01T09:00:00" totalExecutionTimeMs: type: integer format: int64 description: 전체 배포 소요 시간 (ms) example: 1234 message: type: string description: 메시지 example: "배포가 성공적으로 완료되었습니다" ChannelDistributionResult: type: object required: - channel - success properties: channel: type: string description: 채널 타입 enum: - URIDONGNETV - RINGOBIZ - GINITV - INSTAGRAM - NAVER - KAKAO example: "INSTAGRAM" success: type: boolean description: 배포 성공 여부 example: true distributionId: type: string description: 배포 ID (성공 시) example: "dist-uuid-12345" estimatedReach: type: integer description: 예상 노출 수 (성공 시) example: 1500 errorMessage: type: string description: 에러 메시지 (실패 시) example: "Instagram API 타임아웃" executionTimeMs: type: integer format: int64 description: 배포 소요 시간 (ms) example: 234 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: - URIDONGNETV - RINGOBIZ - GINITV - INSTAGRAM - NAVER - KAKAO 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: "dist-uuid-12345" estimatedViews: type: integer description: 예상 노출 수 example: 1500 updateTimestamp: type: string format: date-time description: 업데이트 완료 시각 example: "2025-11-01T09:00:00" 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" 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" completedAt: type: string format: date-time description: 완료 시각 example: "2025-11-01T09:00:00" 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:30" 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