openapi: 3.0.3 info: title: AI Service API description: | KT AI 기반 소상공인 이벤트 자동 생성 서비스 - AI Service ## 서비스 개요 - Kafka를 통한 비동기 AI 추천 처리 - Claude API / GPT-4 API 연동 - Redis 기반 결과 캐싱 (TTL 24시간) ## 처리 흐름 1. Event Service가 Kafka Topic에 Job 메시지 발행 2. AI Service가 메시지 구독 및 처리 3. 트렌드 분석 수행 (Claude/GPT-4 API) 4. 3가지 이벤트 추천안 생성 5. 결과를 Redis에 저장 (TTL 24시간) 6. Job 상태를 Redis에 업데이트 ## 외부 API 통합 - **Claude API / GPT-4 API**: 트렌드 분석 및 이벤트 추천 - **Circuit Breaker**: 5분 타임아웃, Fallback to 캐시 - **Retry**: 최대 3회, Exponential Backoff version: 1.0.0 contact: name: Digital Garage Team email: support@kt-event-marketing.com servers: - url: http://localhost:8083 description: Local Development Server - url: https://dev-api.kt-event-marketing.com/ai/v1 description: Development Server - url: https://api.kt-event-marketing.com/ai/v1 description: Production Server tags: - name: Health Check description: 서비스 상태 확인 - name: Internal API description: 내부 서비스 간 통신용 API - name: Kafka Consumer description: 비동기 작업 처리 (문서화만) paths: /health: get: tags: - Health Check summary: 서비스 헬스체크 description: AI Service 상태 및 외부 연동 확인 operationId: healthCheck x-user-story: System x-controller: HealthController responses: '200': description: 서비스 정상 content: application/json: schema: $ref: '#/components/schemas/HealthCheckResponse' example: status: UP timestamp: "2025-10-23T10:30:00Z" services: kafka: UP redis: UP claude_api: UP gpt4_api: UP circuit_breaker: CLOSED /internal/jobs/{jobId}/status: get: tags: - Internal API summary: 작업 상태 조회 description: Redis에 저장된 AI 추천 작업 상태 조회 (Event Service에서 호출) operationId: getJobStatus x-user-story: UFR-AI-010 x-controller: InternalJobController parameters: - name: jobId in: path required: true schema: type: string description: Job ID example: "job-ai-evt001-20251023103000" responses: '200': description: 작업 상태 조회 성공 content: application/json: schema: $ref: '#/components/schemas/JobStatusResponse' examples: processing: summary: 처리 중 value: jobId: "job-ai-evt001-20251023103000" status: "PROCESSING" progress: 50 message: "AI 추천 생성 중" createdAt: "2025-10-23T10:30:00Z" startedAt: "2025-10-23T10:30:05Z" completed: summary: 완료 value: jobId: "job-ai-evt001-20251023103000" status: "COMPLETED" progress: 100 message: "AI 추천 완료" createdAt: "2025-10-23T10:30:00Z" startedAt: "2025-10-23T10:30:05Z" completedAt: "2025-10-23T10:35:00Z" processingTimeMs: 295000 failed: summary: 실패 value: jobId: "job-ai-evt001-20251023103000" status: "FAILED" progress: 0 message: "Claude API timeout" errorMessage: "Claude API timeout after 5 minutes" createdAt: "2025-10-23T10:30:00Z" startedAt: "2025-10-23T10:30:05Z" failedAt: "2025-10-23T10:35:05Z" retryCount: 3 '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /internal/recommendations/{eventId}: get: tags: - Internal API summary: AI 추천 결과 조회 description: Redis에 캐시된 AI 추천 결과 조회 (Event Service에서 호출) operationId: getRecommendation x-user-story: UFR-AI-010 x-controller: InternalRecommendationController parameters: - name: eventId in: path required: true schema: type: string description: 이벤트 ID example: "evt-001" responses: '200': description: 추천 결과 조회 성공 content: application/json: schema: $ref: '#/components/schemas/AIRecommendationResult' example: eventId: "evt-001" trendAnalysis: industryTrends: - keyword: "프리미엄 디저트" relevance: 0.85 description: "고급 디저트 카페 트렌드 증가" regionalTrends: - keyword: "핫플레이스" relevance: 0.78 description: "강남 신논현역 주변 유동인구 증가" seasonalTrends: - keyword: "가을 시즌" relevance: 0.92 description: "가을 시즌 한정 메뉴 선호도 증가" recommendations: - optionNumber: 1 concept: "프리미엄 경험형" title: "가을 한정 시그니처 디저트 페어링 이벤트" description: "가을 제철 재료를 활용한 시그니처 디저트와 음료 페어링 체험" targetAudience: "20-30대 여성, SNS 활동적인 고객" duration: recommendedDays: 14 recommendedPeriod: "10월 중순 ~ 11월 초" mechanics: type: "EXPERIENCE" details: "디저트+음료 페어링 세트 주문 시 인스타그램 업로드 고객에게 다음 방문 시 사용 가능한 10% 할인권 제공" promotionChannels: - "Instagram" - "카카오톡 채널" - "네이버 플레이스" estimatedCost: min: 300000 max: 500000 breakdown: material: 200000 promotion: 150000 discount: 150000 expectedMetrics: newCustomers: min: 50 max: 80 repeatVisits: min: 30 max: 50 revenueIncrease: min: 15.0 max: 25.0 roi: min: 120.0 max: 180.0 socialEngagement: estimatedPosts: 100 estimatedReach: 5000 differentiator: "프리미엄 경험 제공으로 고객 만족도와 SNS 바이럴 효과 극대화" generatedAt: "2025-10-23T10:35:00Z" expiresAt: "2025-10-24T10:35:00Z" aiProvider: "CLAUDE" '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' components: schemas: # ==================== Health Check ==================== HealthCheckResponse: type: object description: 서비스 헬스체크 응답 required: - status - timestamp - services properties: status: type: string enum: [UP, DOWN, DEGRADED] description: 전체 서비스 상태 example: UP timestamp: type: string format: date-time description: 체크 시각 example: "2025-10-23T10:30:00Z" services: type: object description: 개별 서비스 상태 required: - kafka - redis - claude_api - circuit_breaker properties: kafka: type: string enum: [UP, DOWN] description: Kafka 연결 상태 example: UP redis: type: string enum: [UP, DOWN] description: Redis 연결 상태 example: UP claude_api: type: string enum: [UP, DOWN, CIRCUIT_OPEN] description: Claude API 상태 example: UP gpt4_api: type: string enum: [UP, DOWN, CIRCUIT_OPEN] description: GPT-4 API 상태 (선택) example: UP circuit_breaker: type: string enum: [CLOSED, OPEN, HALF_OPEN] description: Circuit Breaker 상태 example: CLOSED # ==================== Kafka Job Message (문서화만) ==================== KafkaAIJobMessage: type: object description: | **Kafka Topic**: `ai-event-generation-job` **Consumer Group**: `ai-service-consumers` **처리 방식**: 비동기 **최대 처리 시간**: 5분 AI 이벤트 생성 요청 메시지 required: - jobId - eventId - objective - industry - region properties: jobId: type: string description: Job 고유 ID example: "job-ai-evt001-20251023103000" eventId: type: string description: 이벤트 ID (Event Service에서 생성) example: "evt-001" objective: type: string description: 이벤트 목적 enum: - "신규 고객 유치" - "재방문 유도" - "매출 증대" - "브랜드 인지도 향상" example: "신규 고객 유치" industry: type: string description: 업종 example: "음식점" region: type: string description: 지역 (시/구/동) example: "서울 강남구" storeName: type: string description: 매장명 (선택) example: "맛있는 고깃집" targetAudience: type: string description: 목표 고객층 (선택) example: "20-30대 여성" budget: type: integer description: 예산 (원) (선택) example: 500000 requestedAt: type: string format: date-time description: 요청 시각 example: "2025-10-23T10:30:00Z" # ==================== AI Recommendation Result ==================== AIRecommendationResult: type: object description: | **Redis Key**: `ai:recommendation:{eventId}` **TTL**: 86400초 (24시간) AI 이벤트 추천 결과 required: - eventId - trendAnalysis - recommendations - generatedAt - aiProvider properties: eventId: type: string description: 이벤트 ID example: "evt-001" trendAnalysis: $ref: '#/components/schemas/TrendAnalysis' recommendations: type: array description: 추천 이벤트 기획안 (3개) minItems: 3 maxItems: 3 items: $ref: '#/components/schemas/EventRecommendation' generatedAt: type: string format: date-time description: 생성 시각 example: "2025-10-23T10:35:00Z" expiresAt: type: string format: date-time description: 캐시 만료 시각 (생성 시각 + 24시간) example: "2025-10-24T10:35:00Z" aiProvider: type: string enum: [CLAUDE, GPT4] description: 사용된 AI 제공자 example: "CLAUDE" TrendAnalysis: type: object description: 트렌드 분석 결과 (업종/지역/시즌) required: - industryTrends - regionalTrends - seasonalTrends properties: industryTrends: type: array description: 업종 트렌드 키워드 (최대 5개) maxItems: 5 items: type: object required: - keyword - relevance - description properties: keyword: type: string description: 트렌드 키워드 example: "프리미엄 디저트" relevance: type: number format: float minimum: 0 maximum: 1 description: 연관도 (0-1) example: 0.85 description: type: string description: 트렌드 설명 example: "고급 디저트 카페 트렌드 증가" regionalTrends: type: array description: 지역 트렌드 키워드 (최대 5개) maxItems: 5 items: type: object required: - keyword - relevance - description properties: keyword: type: string example: "핫플레이스" relevance: type: number format: float minimum: 0 maximum: 1 example: 0.78 description: type: string example: "강남 신논현역 주변 유동인구 증가" seasonalTrends: type: array description: 시즌 트렌드 키워드 (최대 5개) maxItems: 5 items: type: object required: - keyword - relevance - description properties: keyword: type: string example: "가을 시즌" relevance: type: number format: float minimum: 0 maximum: 1 example: 0.92 description: type: string example: "가을 시즌 한정 메뉴 선호도 증가" EventRecommendation: type: object description: 이벤트 추천안 (차별화된 3가지 옵션) required: - optionNumber - concept - title - description - targetAudience - duration - mechanics - promotionChannels - estimatedCost - expectedMetrics - differentiator properties: optionNumber: type: integer description: 옵션 번호 (1-3) minimum: 1 maximum: 3 example: 1 concept: type: string description: 이벤트 컨셉 example: "프리미엄 경험형" title: type: string description: 이벤트 제목 maxLength: 100 example: "가을 한정 시그니처 디저트 페어링 이벤트" description: type: string description: 이벤트 설명 maxLength: 500 example: "가을 제철 재료를 활용한 시그니처 디저트와 음료 페어링 체험" targetAudience: type: string description: 목표 고객층 example: "20-30대 여성, SNS 활동적인 고객" duration: type: object description: 이벤트 기간 required: - recommendedDays properties: recommendedDays: type: integer description: 권장 진행 일수 minimum: 1 example: 14 recommendedPeriod: type: string description: 권장 진행 시기 example: "10월 중순 ~ 11월 초" mechanics: type: object description: 이벤트 메커니즘 required: - type - details properties: type: type: string enum: [DISCOUNT, GIFT, STAMP, EXPERIENCE, LOTTERY, COMBO] description: 이벤트 유형 example: "EXPERIENCE" details: type: string description: 상세 메커니즘 maxLength: 500 example: "디저트+음료 페어링 세트 주문 시 인스타그램 업로드 고객에게 다음 방문 시 사용 가능한 10% 할인권 제공" promotionChannels: type: array description: 추천 홍보 채널 (최대 5개) maxItems: 5 items: type: string example: - "Instagram" - "카카오톡 채널" - "네이버 플레이스" estimatedCost: type: object description: 예상 비용 required: - min - max properties: min: type: integer description: 최소 비용 (원) minimum: 0 example: 300000 max: type: integer description: 최대 비용 (원) minimum: 0 example: 500000 breakdown: type: object description: 비용 구성 properties: material: type: integer description: 재료비 (원) example: 200000 promotion: type: integer description: 홍보비 (원) example: 150000 discount: type: integer description: 할인 비용 (원) example: 150000 expectedMetrics: $ref: '#/components/schemas/ExpectedMetrics' differentiator: type: string description: 다른 옵션과의 차별점 maxLength: 500 example: "프리미엄 경험 제공으로 고객 만족도와 SNS 바이럴 효과 극대화, 브랜드 이미지 향상에 집중" ExpectedMetrics: type: object description: 예상 성과 지표 required: - newCustomers - revenueIncrease - roi properties: newCustomers: type: object description: 신규 고객 수 required: - min - max properties: min: type: integer minimum: 0 example: 50 max: type: integer minimum: 0 example: 80 repeatVisits: type: object description: 재방문 고객 수 (선택) properties: min: type: integer minimum: 0 example: 30 max: type: integer minimum: 0 example: 50 revenueIncrease: type: object description: 매출 증가율 (%) required: - min - max properties: min: type: number format: float minimum: 0 example: 15.0 max: type: number format: float minimum: 0 example: 25.0 roi: type: object description: ROI - 투자 대비 수익률 (%) required: - min - max properties: min: type: number format: float minimum: 0 example: 120.0 max: type: number format: float minimum: 0 example: 180.0 socialEngagement: type: object description: SNS 참여도 (선택) properties: estimatedPosts: type: integer description: 예상 게시물 수 minimum: 0 example: 100 estimatedReach: type: integer description: 예상 도달 수 minimum: 0 example: 5000 # ==================== Job Status ==================== JobStatusResponse: type: object description: | **Redis Key**: `ai:job:status:{jobId}` **TTL**: 86400초 (24시간) 작업 상태 응답 required: - jobId - status - progress - message - createdAt properties: jobId: type: string description: Job ID example: "job-ai-evt001-20251023103000" status: type: string enum: [PENDING, PROCESSING, COMPLETED, FAILED] description: 작업 상태 example: "COMPLETED" progress: type: integer minimum: 0 maximum: 100 description: 진행률 (%) example: 100 message: type: string description: 상태 메시지 example: "AI 추천 완료" eventId: type: string description: 이벤트 ID example: "evt-001" createdAt: type: string format: date-time description: 작업 생성 시각 example: "2025-10-23T10:30:00Z" startedAt: type: string format: date-time description: 작업 시작 시각 example: "2025-10-23T10:30:05Z" completedAt: type: string format: date-time description: 작업 완료 시각 (완료 시) example: "2025-10-23T10:35:00Z" failedAt: type: string format: date-time description: 작업 실패 시각 (실패 시) example: "2025-10-23T10:35:05Z" errorMessage: type: string description: 에러 메시지 (실패 시) example: "Claude API timeout after 5 minutes" retryCount: type: integer description: 재시도 횟수 minimum: 0 example: 0 processingTimeMs: type: integer description: 처리 시간 (밀리초) minimum: 0 example: 295000 # ==================== Error Response ==================== ErrorResponse: type: object description: 에러 응답 required: - code - message - timestamp properties: code: type: string description: 에러 코드 enum: - AI_SERVICE_ERROR - JOB_NOT_FOUND - RECOMMENDATION_NOT_FOUND - REDIS_ERROR - KAFKA_ERROR - CIRCUIT_BREAKER_OPEN - INTERNAL_ERROR example: "JOB_NOT_FOUND" message: type: string description: 에러 메시지 example: "작업을 찾을 수 없습니다" timestamp: type: string format: date-time description: 에러 발생 시각 example: "2025-10-23T10:30:00Z" details: type: object description: 추가 에러 상세 additionalProperties: true example: jobId: "job-ai-evt001-20251023103000" responses: NotFound: description: 리소스를 찾을 수 없음 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: code: "JOB_NOT_FOUND" message: "작업을 찾을 수 없습니다" timestamp: "2025-10-23T10:30:00Z" InternalServerError: description: 서버 내부 오류 content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: code: "INTERNAL_ERROR" message: "서버 내부 오류가 발생했습니다" timestamp: "2025-10-23T10:30:00Z" # ==================== 기술 구성 문서화 ==================== x-technical-specifications: circuit-breaker: claude-api: failureThreshold: 5 successThreshold: 2 timeout: 300000 resetTimeout: 60000 fallbackStrategy: CACHED_RECOMMENDATION gpt4-api: failureThreshold: 5 successThreshold: 2 timeout: 300000 resetTimeout: 60000 fallbackStrategy: CACHED_RECOMMENDATION redis-cache: patterns: recommendation: "ai:recommendation:{eventId}" jobStatus: "ai:job:status:{jobId}" fallback: "ai:fallback:{industry}:{region}" ttl: recommendation: 86400 jobStatus: 86400 fallback: 604800 kafka: topics: input: "ai-event-generation-job" consumer: groupId: "ai-service-consumers" maxRetries: 3 retryBackoffMs: 5000 maxPollRecords: 10 sessionTimeoutMs: 30000 external-apis: claude: endpoint: "https://api.anthropic.com/v1/messages" model: "claude-3-5-sonnet-20241022" maxTokens: 4096 timeout: 300000 gpt4: endpoint: "https://api.openai.com/v1/chat/completions" model: "gpt-4-turbo-preview" maxTokens: 4096 timeout: 300000