openapi: 3.0.3 info: title: Event Service API description: | KT AI 기반 소상공인 이벤트 자동 생성 서비스 - Event Service API 이벤트 전체 생명주기 관리 (생성, 조회, 수정, 배포, 종료) - AI 기반 이벤트 추천 및 커스터마이징 - 이미지 생성 및 편집 오케스트레이션 - 배포 채널 관리 및 최종 배포 - 이벤트 상태 관리 (DRAFT, PUBLISHED, ENDED) version: 1.0.0 contact: name: Event Service Team email: event-service@kt.com servers: - url: http://localhost:8080 description: Local development server - url: https://api-dev.kt-event.com description: Development server - url: https://api.kt-event.com description: Production server security: - bearerAuth: [] tags: - name: Dashboard description: 대시보드 및 이벤트 목록 조회 - name: Event Creation description: 이벤트 생성 플로우 - name: Event Management description: 이벤트 수정, 삭제, 종료 - name: Job Status description: 비동기 작업 상태 조회 paths: /api/events: get: tags: - Dashboard summary: 이벤트 목록 조회 description: | 사용자의 이벤트 목록을 조회합니다 (대시보드, 전체보기). 필터, 검색, 페이징을 지원합니다. operationId: getEvents x-user-story: UFR-EVENT-010, UFR-EVENT-070 x-controller: EventController.getEvents parameters: - name: status in: query description: 이벤트 상태 필터 (DRAFT, PUBLISHED, ENDED) required: false schema: type: string enum: [DRAFT, PUBLISHED, ENDED] example: PUBLISHED - name: objective in: query description: 이벤트 목적 필터 required: false schema: type: string example: 신규 고객 유치 - name: search in: query description: 검색어 (이벤트명) required: false schema: type: string example: 봄맞이 - name: page in: query description: 페이지 번호 (0부터 시작) required: false schema: type: integer minimum: 0 default: 0 example: 0 - name: size in: query description: 페이지 크기 required: false schema: type: integer minimum: 1 maximum: 100 default: 20 example: 20 - name: sort in: query description: 정렬 기준 (createdAt, startDate, endDate) required: false schema: type: string enum: [createdAt, startDate, endDate] default: createdAt example: createdAt - name: order in: query description: 정렬 순서 (asc, desc) required: false schema: type: string enum: [asc, desc] default: desc example: desc responses: '200': description: 이벤트 목록 조회 성공 content: application/json: schema: $ref: '#/components/schemas/EventListResponse' '401': $ref: '#/components/responses/Unauthorized' '500': $ref: '#/components/responses/InternalServerError' /api/events/{eventId}: get: tags: - Dashboard summary: 이벤트 상세 조회 description: 특정 이벤트의 상세 정보를 조회합니다. operationId: getEvent x-user-story: UFR-EVENT-060 x-controller: EventController.getEvent parameters: - $ref: '#/components/parameters/EventId' responses: '200': description: 이벤트 상세 조회 성공 content: application/json: schema: $ref: '#/components/schemas/EventDetailResponse' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' put: tags: - Event Management summary: 이벤트 수정 description: | 기존 이벤트의 정보를 수정합니다. DRAFT 상태의 이벤트만 전체 수정 가능하며, PUBLISHED 상태에서는 제한적 수정만 가능합니다. operationId: updateEvent x-user-story: UFR-EVENT-060 x-controller: EventController.updateEvent parameters: - $ref: '#/components/parameters/EventId' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateEventRequest' responses: '200': description: 이벤트 수정 성공 content: application/json: schema: $ref: '#/components/schemas/EventDetailResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': description: 이벤트 상태로 인해 수정 불가 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: code: EVENT_NOT_MODIFIABLE message: PUBLISHED 상태의 이벤트는 제한적으로만 수정 가능합니다. '500': $ref: '#/components/responses/InternalServerError' delete: tags: - Event Management summary: 이벤트 삭제 description: | 이벤트를 삭제합니다. DRAFT 상태의 이벤트만 삭제 가능합니다. operationId: deleteEvent x-user-story: UFR-EVENT-070 x-controller: EventController.deleteEvent parameters: - $ref: '#/components/parameters/EventId' responses: '204': description: 이벤트 삭제 성공 '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': description: 이벤트 상태로 인해 삭제 불가 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: code: EVENT_NOT_DELETABLE message: DRAFT 상태의 이벤트만 삭제 가능합니다. '500': $ref: '#/components/responses/InternalServerError' /api/events/objectives: post: tags: - Event Creation summary: 이벤트 목적 선택 (Step 1) description: | 이벤트 생성 플로우의 첫 단계입니다. 사용자가 이벤트 목적을 선택하고 DRAFT 상태의 이벤트를 생성합니다. operationId: selectObjective x-user-story: UFR-EVENT-020 x-controller: EventController.selectObjective requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SelectObjectiveRequest' responses: '201': description: 이벤트 생성 성공 (DRAFT 상태) content: application/json: schema: $ref: '#/components/schemas/EventCreatedResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '500': $ref: '#/components/responses/InternalServerError' /api/events/{eventId}/ai-recommendations: post: tags: - Event Creation summary: AI 추천 요청 (Step 2) description: | AI 서비스에 이벤트 추천 생성을 요청합니다. Kafka Job을 발행하고 jobId를 반환합니다. Job 상태는 /api/jobs/{jobId}로 폴링하여 확인합니다. operationId: requestAiRecommendations x-user-story: UFR-EVENT-030 x-controller: EventController.requestAiRecommendations parameters: - $ref: '#/components/parameters/EventId' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AiRecommendationRequest' responses: '202': description: AI 추천 요청 접수 content: application/json: schema: $ref: '#/components/schemas/JobAcceptedResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '409': description: 이벤트 상태가 적절하지 않음 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: code: INVALID_EVENT_STATE message: DRAFT 상태의 이벤트만 AI 추천을 요청할 수 있습니다. '500': $ref: '#/components/responses/InternalServerError' /api/events/{eventId}/recommendations: put: tags: - Event Creation summary: AI 추천 선택 및 커스터마이징 (Step 2-2) description: | AI가 생성한 추천 중 하나를 선택하고, 필요시 이벤트명, 문구, 기간 등을 커스터마이징합니다. operationId: selectRecommendation x-user-story: UFR-EVENT-030 x-controller: EventController.selectRecommendation parameters: - $ref: '#/components/parameters/EventId' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SelectRecommendationRequest' responses: '200': description: AI 추천 선택 및 커스터마이징 성공 content: application/json: schema: $ref: '#/components/schemas/EventDetailResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '409': description: 이벤트 상태가 적절하지 않음 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: code: INVALID_EVENT_STATE message: AI 추천이 완료된 DRAFT 상태의 이벤트만 선택 가능합니다. '500': $ref: '#/components/responses/InternalServerError' /api/events/{eventId}/images: post: tags: - Event Creation summary: 이미지 생성 요청 (Step 3) description: | Content Service에 이미지 생성을 요청합니다. Kafka Job을 발행하고 jobId를 반환합니다. Job 상태는 /api/jobs/{jobId}로 폴링하여 확인합니다. operationId: requestImageGeneration x-user-story: UFR-CONT-010 x-controller: EventController.requestImageGeneration parameters: - $ref: '#/components/parameters/EventId' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ImageGenerationRequest' responses: '202': description: 이미지 생성 요청 접수 content: application/json: schema: $ref: '#/components/schemas/JobAcceptedResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '409': description: 이벤트 상태가 적절하지 않음 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: code: INVALID_EVENT_STATE message: AI 추천이 선택된 DRAFT 상태의 이벤트만 이미지 생성이 가능합니다. '500': $ref: '#/components/responses/InternalServerError' /api/events/{eventId}/images/{imageId}/select: put: tags: - Event Creation summary: 이미지 선택 (Step 3-2) description: | 생성된 이미지 중 하나를 선택합니다. 선택된 이미지는 이벤트의 대표 이미지로 설정됩니다. operationId: selectImage x-user-story: UFR-CONT-010 x-controller: EventController.selectImage parameters: - $ref: '#/components/parameters/EventId' - name: imageId in: path description: 이미지 ID required: true schema: type: string format: uuid example: "550e8400-e29b-41d4-a716-446655440006" responses: '200': description: 이미지 선택 성공 content: application/json: schema: $ref: '#/components/schemas/EventDetailResponse' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '409': description: 이벤트 또는 이미지 상태가 적절하지 않음 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: code: INVALID_IMAGE_STATE message: 해당 이미지는 이 이벤트에 속하지 않습니다. '500': $ref: '#/components/responses/InternalServerError' /api/events/{eventId}/images/{imageId}/edit: put: tags: - Event Creation summary: 이미지 편집 (Step 3-3) description: | 선택된 이미지를 편집합니다. Content Service에 편집 요청을 보내고 새로운 이미지 URL을 받습니다. operationId: editImage x-user-story: UFR-CONT-020 x-controller: EventController.editImage parameters: - $ref: '#/components/parameters/EventId' - name: imageId in: path description: 이미지 ID required: true schema: type: string format: uuid example: "550e8400-e29b-41d4-a716-446655440006" requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ImageEditRequest' responses: '200': description: 이미지 편집 성공 content: application/json: schema: $ref: '#/components/schemas/ImageEditResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '409': description: 이미지 상태가 적절하지 않음 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: code: IMAGE_NOT_EDITABLE message: 선택된 이미지만 편집 가능합니다. '500': $ref: '#/components/responses/InternalServerError' /api/events/{eventId}/channels: put: tags: - Event Creation summary: 배포 채널 선택 (Step 4) description: | 이벤트를 배포할 채널을 선택합니다. (웹사이트, 카카오톡, Instagram, Facebook 등) operationId: selectChannels x-user-story: UFR-EVENT-040 x-controller: EventController.selectChannels parameters: - $ref: '#/components/parameters/EventId' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SelectChannelsRequest' responses: '200': description: 배포 채널 선택 성공 content: application/json: schema: $ref: '#/components/schemas/EventDetailResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '409': description: 이벤트 상태가 적절하지 않음 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: code: INVALID_EVENT_STATE message: 이미지가 선택된 DRAFT 상태의 이벤트만 채널 선택이 가능합니다. '500': $ref: '#/components/responses/InternalServerError' /api/events/{eventId}/publish: post: tags: - Event Creation summary: 최종 승인 및 배포 (Step 5) description: | 이벤트를 최종 승인하고 선택된 채널에 배포합니다. Distribution Service를 동기 호출하여 배포하고, 이벤트 상태를 PUBLISHED로 변경합니다. Kafka Event (EventCreated)를 발행합니다. operationId: publishEvent x-user-story: UFR-EVENT-050 x-controller: EventController.publishEvent parameters: - $ref: '#/components/parameters/EventId' responses: '200': description: 이벤트 배포 성공 content: application/json: schema: $ref: '#/components/schemas/EventPublishedResponse' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '409': description: 이벤트 상태가 적절하지 않음 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: code: EVENT_NOT_PUBLISHABLE message: 배포 채널이 선택된 DRAFT 상태의 이벤트만 배포 가능합니다. '500': $ref: '#/components/responses/InternalServerError' '503': description: Distribution Service 호출 실패 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: code: DISTRIBUTION_SERVICE_UNAVAILABLE message: 배포 서비스를 일시적으로 사용할 수 없습니다. /api/events/{eventId}/end: post: tags: - Event Management summary: 이벤트 조기 종료 description: | 진행 중인 이벤트를 조기 종료합니다. PUBLISHED 상태의 이벤트만 종료 가능하며, 종료 시 상태가 ENDED로 변경됩니다. operationId: endEvent x-user-story: UFR-EVENT-060 x-controller: EventController.endEvent parameters: - $ref: '#/components/parameters/EventId' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/EndEventRequest' responses: '200': description: 이벤트 종료 성공 content: application/json: schema: $ref: '#/components/schemas/EventDetailResponse' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '409': description: 이벤트 상태로 인해 종료 불가 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: code: EVENT_NOT_ENDABLE message: PUBLISHED 상태의 이벤트만 종료 가능합니다. '500': $ref: '#/components/responses/InternalServerError' /api/jobs/{jobId}: get: tags: - Job Status summary: Job 상태 폴링 description: | 비동기 작업(AI 추천 생성, 이미지 생성)의 상태를 조회합니다. 클라이언트는 COMPLETED 또는 FAILED가 될 때까지 폴링합니다. COMPLETED 시 Redis에서 결과를 조회할 수 있습니다. operationId: getJobStatus x-user-story: UFR-EVENT-030, UFR-CONT-010 x-controller: JobController.getJobStatus parameters: - name: jobId in: path description: Job ID required: true schema: type: string format: uuid example: "550e8400-e29b-41d4-a716-446655440005" responses: '200': description: Job 상태 조회 성공 content: application/json: schema: $ref: '#/components/schemas/JobStatusResponse' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' components: securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT description: JWT 토큰 인증 parameters: EventId: name: eventId in: path description: 이벤트 ID required: true schema: type: string format: uuid example: "550e8400-e29b-41d4-a716-446655440000" schemas: EventListResponse: type: object properties: content: type: array items: $ref: '#/components/schemas/EventSummary' page: $ref: '#/components/schemas/PageInfo' required: - content - page EventSummary: type: object properties: eventId: type: string format: uuid description: 이벤트 ID example: "550e8400-e29b-41d4-a716-446655440000" eventName: type: string description: 이벤트명 example: "봄맞이 20% 할인 이벤트" objective: type: string description: 이벤트 목적 example: "신규 고객 유치" status: type: string enum: [DRAFT, PUBLISHED, ENDED] description: 이벤트 상태 example: "PUBLISHED" startDate: type: string format: date description: 시작일 example: "2025-03-01" endDate: type: string format: date description: 종료일 example: "2025-03-31" thumbnailUrl: type: string format: uri description: 썸네일 이미지 URL example: "https://cdn.kt-event.com/images/event-thumb-001.jpg" createdAt: type: string format: date-time description: 생성일시 example: "2025-02-15T10:30:00Z" required: - eventId - eventName - objective - status - startDate - endDate - createdAt EventDetailResponse: type: object properties: eventId: type: string format: uuid description: 이벤트 ID example: "550e8400-e29b-41d4-a716-446655440000" userId: type: string format: uuid description: 사용자 ID example: "550e8400-e29b-41d4-a716-446655440001" storeId: type: string format: uuid description: 매장 ID example: "550e8400-e29b-41d4-a716-446655440002" eventName: type: string description: 이벤트명 example: "봄맞이 20% 할인 이벤트" objective: type: string description: 이벤트 목적 example: "신규 고객 유치" description: type: string description: 이벤트 설명 example: "봄을 맞이하여 모든 상품 20% 할인 행사를 진행합니다." targetAudience: type: string description: 타겟 고객 example: "20-30대 여성" promotionType: type: string description: 프로모션 유형 example: "할인" discountRate: type: integer description: 할인율 (%) example: 20 startDate: type: string format: date description: 시작일 example: "2025-03-01" endDate: type: string format: date description: 종료일 example: "2025-03-31" status: type: string enum: [DRAFT, PUBLISHED, ENDED] description: 이벤트 상태 example: "PUBLISHED" selectedImageId: type: string format: uuid description: 선택된 이미지 ID example: "550e8400-e29b-41d4-a716-446655440006" selectedImageUrl: type: string format: uri description: 선택된 이미지 URL example: "https://cdn.kt-event.com/images/event-img-001.jpg" generatedImages: type: array description: 생성된 이미지 목록 items: $ref: '#/components/schemas/GeneratedImage' channels: type: array description: 배포 채널 목록 items: type: string example: "WEBSITE" aiRecommendations: type: array description: AI 추천 목록 items: $ref: '#/components/schemas/AiRecommendation' createdAt: type: string format: date-time description: 생성일시 example: "2025-02-15T10:30:00Z" updatedAt: type: string format: date-time description: 수정일시 example: "2025-02-20T14:45:00Z" required: - eventId - userId - storeId - eventName - objective - status - startDate - endDate - createdAt GeneratedImage: type: object properties: imageId: type: string format: uuid description: 이미지 ID example: "550e8400-e29b-41d4-a716-446655440006" imageUrl: type: string format: uri description: 이미지 URL example: "https://cdn.kt-event.com/images/event-img-001.jpg" isSelected: type: boolean description: 선택 여부 example: true createdAt: type: string format: date-time description: 생성일시 example: "2025-02-16T11:00:00Z" required: - imageId - imageUrl - isSelected - createdAt AiRecommendation: type: object properties: recommendationId: type: string format: uuid description: 추천 ID example: "550e8400-e29b-41d4-a716-446655440007" eventName: type: string description: 추천 이벤트명 example: "봄맞이 20% 할인 이벤트" description: type: string description: 추천 설명 example: "봄을 맞이하여 모든 상품 20% 할인 행사를 진행합니다." promotionType: type: string description: 추천 프로모션 유형 example: "할인" targetAudience: type: string description: 추천 타겟 고객 example: "20-30대 여성" isSelected: type: boolean description: 선택 여부 example: true required: - recommendationId - eventName - description - isSelected SelectObjectiveRequest: type: object properties: objective: type: string description: 이벤트 목적 example: "신규 고객 유치" required: - objective EventCreatedResponse: type: object properties: eventId: type: string format: uuid description: 생성된 이벤트 ID example: "550e8400-e29b-41d4-a716-446655440000" status: type: string enum: [DRAFT] description: 이벤트 상태 (항상 DRAFT) example: "DRAFT" objective: type: string description: 선택된 이벤트 목적 example: "신규 고객 유치" createdAt: type: string format: date-time description: 생성일시 example: "2025-02-15T10:30:00Z" required: - eventId - status - objective - createdAt AiRecommendationRequest: type: object properties: storeInfo: type: object description: 매장 정보 (User Service에서 조회) properties: storeId: type: string format: uuid example: "550e8400-e29b-41d4-a716-446655440002" storeName: type: string example: "우진네 고깃집" category: type: string example: "음식점" description: type: string example: "신선한 한우를 제공하는 고깃집" required: - storeId - storeName - category required: - storeInfo JobAcceptedResponse: type: object properties: jobId: type: string format: uuid description: 생성된 Job ID example: "550e8400-e29b-41d4-a716-446655440005" status: type: string enum: [PENDING] description: Job 상태 (초기 상태는 PENDING) example: "PENDING" message: type: string description: 안내 메시지 example: "AI 추천 생성 요청이 접수되었습니다. /api/jobs/{jobId}로 상태를 확인하세요." required: - jobId - status - message JobStatusResponse: type: object properties: jobId: type: string format: uuid description: Job ID example: "550e8400-e29b-41d4-a716-446655440005" jobType: type: string enum: [AI_RECOMMENDATION, IMAGE_GENERATION] description: Job 유형 example: "AI_RECOMMENDATION" status: type: string enum: [PENDING, PROCESSING, COMPLETED, FAILED] description: Job 상태 example: "COMPLETED" progress: type: integer minimum: 0 maximum: 100 description: 진행률 (%) example: 100 resultKey: type: string description: Redis 결과 키 (COMPLETED 시) example: "ai:recommendation:550e8400-e29b-41d4-a716-446655440005" errorMessage: type: string description: 에러 메시지 (FAILED 시) example: "AI 서비스 연결 실패" createdAt: type: string format: date-time description: Job 생성일시 example: "2025-02-15T10:31:00Z" completedAt: type: string format: date-time description: Job 완료일시 example: "2025-02-15T10:31:30Z" required: - jobId - jobType - status - progress - createdAt SelectRecommendationRequest: type: object properties: recommendationId: type: string format: uuid description: 선택한 추천 ID example: "550e8400-e29b-41d4-a716-446655440007" customizations: type: object description: 커스터마이징 항목 properties: eventName: type: string description: 수정된 이벤트명 example: "봄맞이 특별 할인 이벤트" description: type: string description: 수정된 설명 example: "봄을 맞이하여 전 메뉴 20% 할인" startDate: type: string format: date description: 수정된 시작일 example: "2025-03-01" endDate: type: string format: date description: 수정된 종료일 example: "2025-03-31" discountRate: type: integer description: 수정된 할인율 example: 20 required: - recommendationId ImageGenerationRequest: type: object properties: eventInfo: type: object description: 이벤트 정보 (이미지 생성에 필요한 정보) properties: eventName: type: string example: "봄맞이 20% 할인 이벤트" description: type: string example: "봄을 맞이하여 모든 상품 20% 할인 행사를 진행합니다." promotionType: type: string example: "할인" required: - eventName - description imageCount: type: integer minimum: 1 maximum: 5 description: 생성할 이미지 개수 default: 3 example: 3 required: - eventInfo ImageEditRequest: type: object properties: editType: type: string enum: [TEXT_OVERLAY, COLOR_ADJUST, CROP, FILTER] description: 편집 유형 example: "TEXT_OVERLAY" parameters: type: object description: 편집 파라미터 (편집 유형에 따라 다름) additionalProperties: true example: text: "20% 할인" fontSize: 48 color: "#FF0000" position: "center" required: - editType - parameters ImageEditResponse: type: object properties: imageId: type: string format: uuid description: 편집된 이미지 ID example: "550e8400-e29b-41d4-a716-446655440008" imageUrl: type: string format: uri description: 편집된 이미지 URL example: "https://cdn.kt-event.com/images/event-img-001-edited.jpg" editedAt: type: string format: date-time description: 편집일시 example: "2025-02-16T15:20:00Z" required: - imageId - imageUrl - editedAt SelectChannelsRequest: type: object properties: channels: type: array description: 배포 채널 목록 items: type: string enum: [WEBSITE, KAKAO, INSTAGRAM, FACEBOOK, NAVER_BLOG] example: ["WEBSITE", "KAKAO", "INSTAGRAM"] minItems: 1 required: - channels EventPublishedResponse: type: object properties: eventId: type: string format: uuid description: 이벤트 ID example: "550e8400-e29b-41d4-a716-446655440000" status: type: string enum: [PUBLISHED] description: 이벤트 상태 (항상 PUBLISHED) example: "PUBLISHED" publishedAt: type: string format: date-time description: 배포일시 example: "2025-02-20T16:00:00Z" channels: type: array description: 배포된 채널 목록 items: type: string example: "WEBSITE" distributionResults: type: array description: 채널별 배포 결과 items: $ref: '#/components/schemas/DistributionResult' required: - eventId - status - publishedAt - channels - distributionResults DistributionResult: type: object properties: channel: type: string description: 채널명 example: "WEBSITE" success: type: boolean description: 배포 성공 여부 example: true url: type: string format: uri description: 배포된 URL example: "https://store.kt-event.com/event/550e8400-e29b-41d4-a716-446655440000" message: type: string description: 배포 결과 메시지 example: "웹사이트에 성공적으로 배포되었습니다." required: - channel - success UpdateEventRequest: type: object properties: eventName: type: string description: 이벤트명 example: "봄맞이 특별 할인 이벤트" description: type: string description: 이벤트 설명 example: "봄을 맞이하여 전 메뉴 20% 할인" startDate: type: string format: date description: 시작일 example: "2025-03-01" endDate: type: string format: date description: 종료일 example: "2025-03-31" discountRate: type: integer description: 할인율 example: 20 EndEventRequest: type: object properties: reason: type: string description: 종료 사유 example: "목표 달성으로 조기 종료" required: - reason PageInfo: type: object properties: page: type: integer description: 현재 페이지 번호 example: 0 size: type: integer description: 페이지 크기 example: 20 totalElements: type: integer description: 전체 요소 개수 example: 45 totalPages: type: integer description: 전체 페이지 개수 example: 3 required: - page - size - totalElements - totalPages ErrorResponse: type: object properties: code: type: string description: 에러 코드 example: "INVALID_REQUEST" message: type: string description: 에러 메시지 example: "요청 파라미터가 올바르지 않습니다." details: type: array description: 상세 에러 정보 items: type: string example: ["objective 필드는 필수입니다."] timestamp: type: string format: date-time description: 에러 발생 시각 example: "2025-02-15T10:30:00Z" required: - code - message - timestamp responses: BadRequest: description: 잘못된 요청 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: code: INVALID_REQUEST message: 요청 파라미터가 올바르지 않습니다. details: - "objective 필드는 필수입니다." timestamp: "2025-02-15T10:30:00Z" Unauthorized: description: 인증 실패 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: code: UNAUTHORIZED message: 인증에 실패했습니다. timestamp: "2025-02-15T10:30:00Z" Forbidden: description: 권한 없음 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: code: FORBIDDEN message: 해당 리소스에 접근할 권한이 없습니다. timestamp: "2025-02-15T10:30:00Z" NotFound: description: 리소스를 찾을 수 없음 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: code: NOT_FOUND message: 요청한 리소스를 찾을 수 없습니다. timestamp: "2025-02-15T10:30:00Z" InternalServerError: description: 서버 내부 오류 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: code: INTERNAL_SERVER_ERROR message: 서버 내부 오류가 발생했습니다. timestamp: "2025-02-15T10:30:00Z"