openapi: 3.0.3 info: title: Analytics Service API description: | KT AI 기반 소상공인 이벤트 자동 생성 서비스 - Analytics Service API 이벤트 성과 분석 및 통합 대시보드를 제공하는 서비스입니다. **주요 기능:** - 실시간 성과 분석 대시보드 (UFR-ANAL-010) - 채널별 성과 추적 - ROI 계산 및 비교 분석 - 참여자 프로필 분석 **데이터 소스:** - Participation Service: 참여자 데이터 - Distribution Service: 채널별 노출 수 - 외부 API: 우리동네TV, 지니TV, SNS 통계 - POS 시스템: 매출 데이터 (연동 시) version: 1.0.0 contact: name: Analytics Service Team email: analytics@kt-event-service.com servers: - url: https://api.kt-event-service.com/analytics/v1 description: Production Server - url: https://dev-api.kt-event-service.com/analytics/v1 description: Development Server - url: http://localhost:8084/api description: Local Development Server tags: - name: Analytics description: 성과 분석 대시보드 API - name: Health description: 서비스 헬스 체크 paths: /health: get: tags: - Health summary: 헬스 체크 description: Analytics Service의 상태를 확인합니다. operationId: healthCheck responses: '200': description: 서비스 정상 content: application/json: schema: type: object properties: status: type: string example: "UP" service: type: string example: "analytics-service" timestamp: type: string format: date-time example: "2025-10-22T10:00:00Z" /events/{eventId}/analytics: get: tags: - Analytics summary: 이벤트 성과 분석 대시보드 조회 description: | 특정 이벤트의 실시간 성과 분석 데이터를 조회합니다. **유저스토리:** UFR-ANAL-010 - 성과 분석 대시보드 **주요 기능:** - 4개 요약 카드 (참여자 수, 노출 수, ROI, 매출 증가율) - 채널별 성과 분석 - 시간대별 참여 추이 - 참여자 프로필 분석 - 비교 분석 (업종 평균, 이전 이벤트) **캐싱 전략:** - Redis Cache-Aside 패턴 - TTL: 300초 (5분) - Cache HIT 시 응답 시간: 약 0.5초 - Cache MISS 시 응답 시간: 약 3초 **데이터 업데이트:** - 실시간 업데이트: Kafka 이벤트 구독 - EventCreated: 통계 초기화 - ParticipantRegistered: 참여자 수 증가 - DistributionCompleted: 배포 통계 업데이트 operationId: getEventAnalytics security: - BearerAuth: [] parameters: - name: eventId in: path required: true description: 이벤트 ID (UUID) schema: type: string format: uuid example: "550e8400-e29b-41d4-a716-446655440000" responses: '200': description: 대시보드 데이터 조회 성공 content: application/json: schema: $ref: '#/components/schemas/DashboardResponse' examples: success: summary: 성공 응답 예시 value: eventId: "550e8400-e29b-41d4-a716-446655440000" storeId: "660e8400-e29b-41d4-a716-446655440001" eventTitle: "신규 고객 환영 이벤트" summaryCards: totalParticipants: count: 1234 targetGoal: 1000 achievementRate: 123.4 dailyChange: 150 totalViews: count: 17200 yesterdayChange: 5.2 channelBreakdown: - channel: "우리동네TV" views: 5000 - channel: "지니TV" views: 10000 - channel: "Instagram" views: 2000 - channel: "Naver Blog" views: 200 roi: value: 250.0 industryAverage: 180.0 comparisonRate: 138.9 totalCost: 1000000 totalRevenue: 3500000 breakEvenStatus: "ACHIEVED" salesGrowth: rate: 15.2 beforeEventSales: 5000000 afterEventSales: 5760000 periodComparison: "이벤트 전후 7일 비교" channelPerformance: - channel: "우리동네TV" views: 5000 participants: 400 conversionRate: 8.0 costPerAcquisition: 2500 status: "SUCCESS" - channel: "지니TV" views: 10000 participants: 500 conversionRate: 5.0 costPerAcquisition: 4000 status: "SUCCESS" - channel: "Instagram" views: 2000 participants: 200 conversionRate: 10.0 costPerAcquisition: 1000 status: "SUCCESS" - channel: "Naver Blog" views: 200 participants: 100 conversionRate: 50.0 costPerAcquisition: 500 status: "SUCCESS" - channel: "Kakao Channel" views: 0 participants: 34 conversionRate: 0 costPerAcquisition: 0 status: "SUCCESS" participationTrend: timeUnit: "DAILY" dataPoints: - timestamp: "2025-10-15T00:00:00Z" participantCount: 100 - timestamp: "2025-10-16T00:00:00Z" participantCount: 250 - timestamp: "2025-10-17T00:00:00Z" participantCount: 400 - timestamp: "2025-10-18T00:00:00Z" participantCount: 600 - timestamp: "2025-10-19T00:00:00Z" participantCount: 850 - timestamp: "2025-10-20T00:00:00Z" participantCount: 1050 - timestamp: "2025-10-21T00:00:00Z" participantCount: 1234 peakTime: timestamp: "2025-10-20T18:00:00Z" participantCount: 200 roiAnalysis: totalCost: prizeCost: 500000 channelCosts: - channel: "우리동네TV" cost: 100000 - channel: "지니TV" cost: 200000 - channel: "Instagram" cost: 50000 - channel: "Naver Blog" cost: 50000 - channel: "Kakao Channel" cost: 0 otherCosts: 100000 total: 1000000 expectedRevenue: salesIncrease: 760000 newCustomerLTV: 2740000 total: 3500000 roiCalculation: formula: "(수익 - 비용) / 비용 × 100" roi: 250.0 breakEvenPoint: required: 1000000 achieved: 3500000 status: "ACHIEVED" participantProfile: ageDistribution: - ageGroup: "10대" count: 50 percentage: 4.1 - ageGroup: "20대" count: 300 percentage: 24.3 - ageGroup: "30대" count: 450 percentage: 36.5 - ageGroup: "40대" count: 300 percentage: 24.3 - ageGroup: "50대 이상" count: 134 percentage: 10.8 genderDistribution: - gender: "남성" count: 600 percentage: 48.6 - gender: "여성" count: 634 percentage: 51.4 regionDistribution: - region: "서울" count: 500 percentage: 40.5 - region: "경기" count: 400 percentage: 32.4 - region: "기타" count: 334 percentage: 27.1 timeDistribution: - timeSlot: "오전 (06:00-12:00)" count: 200 percentage: 16.2 - timeSlot: "오후 (12:00-18:00)" count: 500 percentage: 40.5 - timeSlot: "저녁 (18:00-24:00)" count: 500 percentage: 40.5 - timeSlot: "새벽 (00:00-06:00)" count: 34 percentage: 2.8 comparativeAnalysis: industryComparison: - metric: "참여율" myValue: 7.2 industryAverage: 5.5 percentageDifference: 30.9 - metric: "ROI" myValue: 250.0 industryAverage: 180.0 percentageDifference: 38.9 - metric: "전환율" myValue: 8.5 industryAverage: 6.0 percentageDifference: 41.7 previousEventComparison: - metric: "참여자 수" currentValue: 1234 previousBest: 1000 improvementRate: 23.4 - metric: "ROI" currentValue: 250.0 previousBest: 200.0 improvementRate: 25.0 - metric: "매출 증가율" currentValue: 15.2 previousBest: 12.0 improvementRate: 26.7 lastUpdated: "2025-10-22T10:30:00Z" cacheStatus: "HIT" '400': description: 잘못된 요청 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' examples: invalidEventId: summary: 잘못된 이벤트 ID value: error: "INVALID_REQUEST" message: "유효하지 않은 이벤트 ID 형식입니다." timestamp: "2025-10-22T10:00:00Z" '401': description: 인증 실패 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' examples: unauthorized: summary: 인증되지 않은 요청 value: error: "UNAUTHORIZED" message: "인증이 필요합니다." timestamp: "2025-10-22T10:00:00Z" '403': description: 권한 없음 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' examples: forbidden: summary: 권한 없음 value: error: "FORBIDDEN" message: "해당 이벤트의 통계를 조회할 권한이 없습니다." timestamp: "2025-10-22T10:00:00Z" '404': description: 이벤트를 찾을 수 없음 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' examples: notFound: summary: 이벤트 미존재 value: error: "EVENT_NOT_FOUND" message: "해당 이벤트를 찾을 수 없습니다." timestamp: "2025-10-22T10:00:00Z" '500': description: 서버 내부 오류 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' examples: internalError: summary: 서버 오류 value: error: "INTERNAL_SERVER_ERROR" message: "서버 내부 오류가 발생했습니다." timestamp: "2025-10-22T10:00:00Z" '503': description: 외부 API 서비스 이용 불가 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' examples: externalServiceUnavailable: summary: 외부 서비스 장애 value: error: "EXTERNAL_SERVICE_UNAVAILABLE" message: "일부 채널 데이터를 불러올 수 없습니다. Fallback 데이터를 사용합니다." timestamp: "2025-10-22T10:00:00Z" components: securitySchemes: BearerAuth: type: http scheme: bearer bearerFormat: JWT description: JWT 토큰 기반 인증. Authorization 헤더에 "Bearer {token}" 형식으로 전달합니다. schemas: DashboardResponse: type: object required: - eventId - storeId - eventTitle - summaryCards - channelPerformance - participationTrend - roiAnalysis - participantProfile - comparativeAnalysis - lastUpdated properties: eventId: type: string format: uuid description: 이벤트 ID example: "550e8400-e29b-41d4-a716-446655440000" storeId: type: string format: uuid description: 매장 ID example: "660e8400-e29b-41d4-a716-446655440001" eventTitle: type: string description: 이벤트 제목 example: "신규 고객 환영 이벤트" summaryCards: $ref: '#/components/schemas/SummaryCards' channelPerformance: type: array description: 채널별 성과 분석 items: $ref: '#/components/schemas/ChannelPerformance' participationTrend: $ref: '#/components/schemas/ParticipationTrend' roiAnalysis: $ref: '#/components/schemas/ROIAnalysis' participantProfile: $ref: '#/components/schemas/ParticipantProfile' comparativeAnalysis: $ref: '#/components/schemas/ComparativeAnalysis' lastUpdated: type: string format: date-time description: 마지막 업데이트 시각 example: "2025-10-22T10:30:00Z" cacheStatus: type: string enum: [HIT, MISS] description: 캐시 상태 (HIT/MISS) example: "HIT" SummaryCards: type: object description: 4개 요약 카드 required: - totalParticipants - totalViews - roi - salesGrowth properties: totalParticipants: $ref: '#/components/schemas/TotalParticipantsCard' totalViews: $ref: '#/components/schemas/TotalViewsCard' roi: $ref: '#/components/schemas/ROICard' salesGrowth: $ref: '#/components/schemas/SalesGrowthCard' TotalParticipantsCard: type: object description: 총 참여자 수 카드 required: - count - targetGoal - achievementRate properties: count: type: integer description: 현재 참여자 수 example: 1234 targetGoal: type: integer description: 목표 참여자 수 example: 1000 achievementRate: type: number format: float description: 목표 대비 달성률 (%) example: 123.4 dailyChange: type: integer description: 전일 대비 증가 수 example: 150 TotalViewsCard: type: object description: 총 노출 수 카드 required: - count - yesterdayChange properties: count: type: integer description: 총 노출 수 (채널별 노출 합계) example: 17200 yesterdayChange: type: number format: float description: 전일 대비 증감률 (%) example: 5.2 channelBreakdown: type: array description: 채널별 노출 수 분포 items: type: object properties: channel: type: string description: 채널명 example: "우리동네TV" views: type: integer description: 노출 수 example: 5000 ROICard: type: object description: 예상 투자 대비 수익률 카드 required: - value - industryAverage - comparisonRate - totalCost - totalRevenue - breakEvenStatus properties: value: type: number format: float description: 실시간 ROI (%) example: 250.0 industryAverage: type: number format: float description: 업종 평균 ROI (%) example: 180.0 comparisonRate: type: number format: float description: 업종 평균 대비 비율 (%) example: 138.9 totalCost: type: integer description: 총 비용 (원) example: 1000000 totalRevenue: type: integer description: 총 수익 (원) example: 3500000 breakEvenStatus: type: string enum: [ACHIEVED, NOT_ACHIEVED] description: 손익분기점 달성 여부 example: "ACHIEVED" SalesGrowthCard: type: object description: 매출 증가율 카드 required: - rate properties: rate: type: number format: float description: 매출 증가율 (%) example: 15.2 beforeEventSales: type: integer description: 이벤트 전 매출 (원) example: 5000000 afterEventSales: type: integer description: 이벤트 후 매출 (원) example: 5760000 periodComparison: type: string description: 비교 기간 설명 example: "이벤트 전후 7일 비교" ChannelPerformance: type: object description: 채널별 성과 분석 required: - channel - views - participants - conversionRate - costPerAcquisition - status properties: channel: type: string description: 채널명 example: "우리동네TV" views: type: integer description: 노출 수 example: 5000 participants: type: integer description: 참여자 수 example: 400 conversionRate: type: number format: float description: 전환율 (%) example: 8.0 costPerAcquisition: type: integer description: 비용 대비 효율 (CPA, 원) example: 2500 status: type: string enum: [SUCCESS, FAILED, PENDING] description: 배포 상태 example: "SUCCESS" ParticipationTrend: type: object description: 시간대별 참여 추이 required: - timeUnit - dataPoints properties: timeUnit: type: string enum: [HOURLY, DAILY] description: 시간 단위 (시간별/일별) example: "DAILY" dataPoints: type: array description: 시간대별 데이터 포인트 items: type: object properties: timestamp: type: string format: date-time description: 시각 example: "2025-10-15T00:00:00Z" participantCount: type: integer description: 참여자 수 example: 100 peakTime: type: object description: 피크 시간대 properties: timestamp: type: string format: date-time description: 피크 시각 example: "2025-10-20T18:00:00Z" participantCount: type: integer description: 피크 참여자 수 example: 200 ROIAnalysis: type: object description: 투자 대비 수익률 상세 분석 required: - totalCost - expectedRevenue - roiCalculation - breakEvenPoint properties: totalCost: $ref: '#/components/schemas/TotalCost' expectedRevenue: $ref: '#/components/schemas/ExpectedRevenue' roiCalculation: $ref: '#/components/schemas/ROICalculation' breakEvenPoint: $ref: '#/components/schemas/BreakEvenPoint' TotalCost: type: object description: 총 비용 산출 required: - prizeCost - channelCosts - otherCosts - total properties: prizeCost: type: integer description: 경품 비용 (원) example: 500000 channelCosts: type: array description: 채널별 플랫폼 비용 items: type: object properties: channel: type: string description: 채널명 example: "우리동네TV" cost: type: integer description: 비용 (원) example: 100000 otherCosts: type: integer description: 기타 비용 (원) example: 100000 total: type: integer description: 총 비용 (원) example: 1000000 ExpectedRevenue: type: object description: 예상 수익 산출 required: - salesIncrease - newCustomerLTV - total properties: salesIncrease: type: integer description: 매출 증가액 (원) example: 760000 newCustomerLTV: type: integer description: 신규 고객 LTV (원) example: 2740000 total: type: integer description: 총 예상 수익 (원) example: 3500000 ROICalculation: type: object description: ROI 계산 required: - formula - roi properties: formula: type: string description: ROI 계산 공식 example: "(수익 - 비용) / 비용 × 100" roi: type: number format: float description: ROI (%) example: 250.0 BreakEvenPoint: type: object description: 손익분기점 required: - required - achieved - status properties: required: type: integer description: 손익분기점 (원) example: 1000000 achieved: type: integer description: 달성 금액 (원) example: 3500000 status: type: string enum: [ACHIEVED, NOT_ACHIEVED] description: 달성 여부 example: "ACHIEVED" ParticipantProfile: type: object description: 참여자 프로필 분석 required: - ageDistribution - genderDistribution - regionDistribution - timeDistribution properties: ageDistribution: type: array description: 연령대별 분포 items: type: object properties: ageGroup: type: string description: 연령대 example: "20대" count: type: integer description: 인원 수 example: 300 percentage: type: number format: float description: 비율 (%) example: 24.3 genderDistribution: type: array description: 성별 분포 items: type: object properties: gender: type: string description: 성별 example: "남성" count: type: integer description: 인원 수 example: 600 percentage: type: number format: float description: 비율 (%) example: 48.6 regionDistribution: type: array description: 지역별 분포 items: type: object properties: region: type: string description: 지역 example: "서울" count: type: integer description: 인원 수 example: 500 percentage: type: number format: float description: 비율 (%) example: 40.5 timeDistribution: type: array description: 참여 시간대 분석 items: type: object properties: timeSlot: type: string description: 시간대 example: "오전 (06:00-12:00)" count: type: integer description: 참여자 수 example: 200 percentage: type: number format: float description: 비율 (%) example: 16.2 ComparativeAnalysis: type: object description: 비교 분석 required: - industryComparison - previousEventComparison properties: industryComparison: type: array description: 업종 평균과 비교 items: type: object properties: metric: type: string description: 지표명 example: "참여율" myValue: type: number format: float description: 내 값 example: 7.2 industryAverage: type: number format: float description: 업종 평균 example: 5.5 percentageDifference: type: number format: float description: 차이율 (%) example: 30.9 previousEventComparison: type: array description: 내 이전 이벤트와 비교 items: type: object properties: metric: type: string description: 지표명 example: "참여자 수" currentValue: type: number format: float description: 현재 값 example: 1234 previousBest: type: number format: float description: 이전 최고 기록 example: 1000 improvementRate: type: number format: float description: 개선율 (%) example: 23.4 ErrorResponse: type: object description: 에러 응답 required: - error - message - timestamp properties: error: type: string description: 에러 코드 example: "INVALID_REQUEST" message: type: string description: 에러 메시지 example: "유효하지 않은 요청입니다." timestamp: type: string format: date-time description: 에러 발생 시각 example: "2025-10-22T10:00:00Z"