kt-event-marketing/design/backend/api/ai-service-api.yaml
2025-10-22 16:37:32 +09:00

1037 lines
32 KiB
YAML

openapi: 3.0.3
info:
title: AI Service API
description: |
AI 기반 트렌드 분석 및 이벤트 추천 서비스 API
## 주요 기능
- 업종/지역/시즌별 트렌드 분석
- AI 기반 이벤트 기획안 추천 (3가지 옵션)
- 비동기 Job 처리 및 폴링 기반 결과 조회
## 기술 스택
- AI Engine: Claude API / GPT-4 API
- 캐싱: Redis (트렌드 1시간, 추천안 24시간)
- 메시지 큐: Kafka (비동기 Job 처리)
- 안정성: Circuit Breaker 패턴
version: 1.0.0
contact:
name: AI Service Team
email: ai-team@kt.com
servers:
- url: https://api.kt-event.com/ai/v1
description: 프로덕션 서버
- url: https://dev-api.kt-event.com/ai/v1
description: 개발 서버
- url: http://localhost:8083/ai/v1
description: 로컬 개발 서버
tags:
- name: AI Analysis
description: AI 기반 트렌드 분석 및 추천 엔드포인트
- name: Job Status
description: 비동기 Job 상태 조회 엔드포인트
paths:
/analyze-trends:
post:
tags:
- AI Analysis
summary: 트렌드 분석 요청
description: |
업종, 지역, 시즌을 기반으로 트렌드 분석을 수행합니다.
## 처리 방식
- **비동기 처리**: Kafka를 통한 비동기 Job 생성
- **응답 시간**: 즉시 Job ID 반환 (< 100ms)
- **실제 처리 시간**: 5~30초 이내 (AI API 응답 시간 포함)
## 캐싱 전략
- 캐시 키: `trend:{업종}:{지역}`
- TTL: 1시간
- 캐시 히트 시 즉시 응답
## Circuit Breaker
- Failure Rate Threshold: 50%
- Timeout: 30초
- Half-Open Wait Duration: 30초
operationId: analyzeTrends
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/TrendAnalysisRequest"
examples:
restaurant:
summary: 음식점 트렌드 분석
value:
eventDraftId: "evt_draft_001"
industry: "음식점"
region: "서울 강남구"
purpose: "신규 고객 유치"
storeInfo:
storeName: "맛있는 고깃집"
storeSize: "중형"
monthlyRevenue: 30000000
cafe:
summary: 카페 트렌드 분석
value:
eventDraftId: "evt_draft_002"
industry: "카페"
region: "서울 홍대"
purpose: "재방문 유도"
storeInfo:
storeName: "커피스토리"
storeSize: "소형"
monthlyRevenue: 15000000
responses:
"202":
description: |
트렌드 분석 Job이 생성되었습니다.
- Job ID를 사용하여 `/jobs/{jobId}` 엔드포인트로 결과 폴링
- 예상 처리 시간: 5~30초
content:
application/json:
schema:
$ref: "#/components/schemas/JobCreatedResponse"
example:
jobId: "job_ai_20250122_001"
status: "PROCESSING"
message: "트렌드 분석이 진행 중입니다"
estimatedCompletionTime: "2025-01-22T10:05:30Z"
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
"429":
$ref: "#/components/responses/TooManyRequests"
"500":
$ref: "#/components/responses/InternalServerError"
/recommend-events:
post:
tags:
- AI Analysis
summary: 이벤트 추천 요청
description: |
트렌드 분석 결과를 기반으로 3가지 차별화된 이벤트 기획안을 생성합니다.
## 처리 방식
- **비동기 처리**: Kafka를 통한 비동기 Job 생성
- **응답 시간**: 즉시 Job ID 반환 (< 100ms)
- **실제 처리 시간**: 5~30초 이내
## 3가지 추천 옵션
1. **저비용 옵션**: 높은 참여율 중심
2. **중비용 옵션**: 균형잡힌 ROI
3. **고비용 옵션**: 높은 매출 증대 효과
## 병렬 처리
- 3가지 옵션 동시 생성 (병렬)
- 전체 처리 시간: 단일 요청 시간과 동일
## 캐싱 전략
- 캐시 키: `ai:recommendation:{eventDraftId}`
- TTL: 24시간
operationId: recommendEvents
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/EventRecommendationRequest"
examples:
newCustomer:
summary: 신규 고객 유치 이벤트
value:
eventDraftId: "evt_draft_001"
purpose: "신규 고객 유치"
industry: "음식점"
region: "서울 강남구"
storeInfo:
storeName: "맛있는 고깃집"
storeSize: "중형"
monthlyRevenue: 30000000
trendAnalysisJobId: "job_ai_20250122_001"
responses:
"202":
description: |
이벤트 추천 Job이 생성되었습니다.
- Job ID를 사용하여 `/jobs/{jobId}` 엔드포인트로 결과 폴링
- 예상 처리 시간: 5~30초
content:
application/json:
schema:
$ref: "#/components/schemas/JobCreatedResponse"
example:
jobId: "job_ai_20250122_002"
status: "PROCESSING"
message: "이벤트 추천이 진행 중입니다"
estimatedCompletionTime: "2025-01-22T10:05:30Z"
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
"429":
$ref: "#/components/responses/TooManyRequests"
"500":
$ref: "#/components/responses/InternalServerError"
/jobs/{jobId}:
get:
tags:
- Job Status
summary: Job 상태 조회
description: |
비동기 Job의 처리 상태 및 결과를 조회합니다.
## 폴링 전략
- **초기 폴링**: 1초 간격 (처음 5회)
- **장기 폴링**: 3초 간격 (이후)
- **최대 대기 시간**: 60초
## Job 상태
- `PENDING`: 대기 중
- `PROCESSING`: 처리 중
- `COMPLETED`: 완료
- `FAILED`: 실패
## 캐싱
- Redis를 통한 Job 상태 저장
- 키: `job:{jobId}`
operationId: getJobStatus
security:
- bearerAuth: []
parameters:
- name: jobId
in: path
required: true
description: Job ID
schema:
type: string
example: "job_ai_20250122_001"
responses:
"200":
description: Job 상태 조회 성공
content:
application/json:
schema:
oneOf:
- $ref: "#/components/schemas/TrendAnalysisJobResponse"
- $ref: "#/components/schemas/EventRecommendationJobResponse"
examples:
processing:
summary: 처리 중
value:
jobId: "job_ai_20250122_001"
status: "PROCESSING"
message: "트렌드 분석 중입니다"
progress: 50
createdAt: "2025-01-22T10:05:00Z"
estimatedCompletionTime: "2025-01-22T10:05:30Z"
trendCompleted:
summary: 트렌드 분석 완료
value:
jobId: "job_ai_20250122_001"
status: "COMPLETED"
message: "트렌드 분석이 완료되었습니다"
result:
industryTrends:
successfulEventTypes:
- type: "할인 이벤트"
successRate: 85
- type: "경품 추첨"
successRate: 78
popularPrizes:
- prize: "커피 쿠폰"
preferenceScore: 92
- prize: "현금 할인"
preferenceScore: 88
effectiveParticipationMethods:
- method: "간단한 설문조사"
engagementRate: 75
regionalCharacteristics:
successRate: 82
demographicProfile:
ageGroups:
- range: "20-29"
percentage: 35
- range: "30-39"
percentage: 40
genderDistribution:
male: 45
female: 55
seasonalPatterns:
currentSeason: "겨울"
recommendedEventTypes:
- "따뜻한 음료 할인"
- "연말 감사 이벤트"
specialOccasions:
- occasion: "설날"
daysUntil: 15
completedAt: "2025-01-22T10:05:25Z"
recommendationCompleted:
summary: 이벤트 추천 완료
value:
jobId: "job_ai_20250122_002"
status: "COMPLETED"
message: "이벤트 추천이 완료되었습니다"
result:
recommendations:
- option: 1
title: "신규 고객 환영 커피 쿠폰 증정"
budget: "low"
prize:
name: "아메리카노 쿠폰"
quantity: 100
estimatedCost: 300000
participationMethod:
type: "간단한 설문조사"
difficulty: "low"
description: "매장 방문 후 QR 코드 스캔 및 간단한 설문"
estimatedParticipants: 150
estimatedROI: 250
promotionalTexts:
- "따뜻한 커피 한 잔으로 시작하는 하루!"
- "신규 방문 고객님께 특별한 선물"
hashtags:
- "#강남맛집"
- "#커피쿠폰"
- "#신규고객환영"
- option: 2
title: "런치 세트 20% 할인 이벤트"
budget: "medium"
prize:
name: "런치 세트 할인권"
quantity: 50
estimatedCost: 500000
participationMethod:
type: "재방문 미션"
difficulty: "medium"
description: "첫 방문 후 리뷰 작성 시 할인권 제공"
estimatedParticipants: 80
estimatedROI: 320
promotionalTexts:
- "점심 시간, 특별한 할인 기회!"
- "리뷰 남기고 할인받자"
hashtags:
- "#강남맛집"
- "#런치할인"
- "#점심특가"
- option: 3
title: "디너 코스 무료 업그레이드 추첨"
budget: "high"
prize:
name: "디너 코스 업그레이드권"
quantity: 10
estimatedCost: 1000000
participationMethod:
type: "바이럴 확산"
difficulty: "high"
description: "SNS 공유 및 친구 태그 3명 이상"
estimatedParticipants: 200
estimatedROI: 450
promotionalTexts:
- "프리미엄 디너 코스로 업그레이드!"
- "친구와 함께 즐기는 특별한 저녁"
hashtags:
- "#강남맛집"
- "#디너코스"
- "#프리미엄디너"
completedAt: "2025-01-22T10:05:35Z"
failed:
summary: 처리 실패
value:
jobId: "job_ai_20250122_003"
status: "FAILED"
message: "AI API 오류로 인해 처리에 실패했습니다"
error:
code: "AI_API_ERROR"
detail: "External AI API timeout after 30 seconds"
failedAt: "2025-01-22T10:05:45Z"
"404":
$ref: "#/components/responses/NotFound"
"401":
$ref: "#/components/responses/Unauthorized"
"500":
$ref: "#/components/responses/InternalServerError"
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: |
JWT 토큰 기반 인증
- User Service에서 발급한 JWT 토큰 사용
- 헤더: `Authorization: Bearer {token}`
schemas:
TrendAnalysisRequest:
type: object
required:
- eventDraftId
- industry
- region
- purpose
properties:
eventDraftId:
type: string
description: 이벤트 초안 ID (Event Service에서 생성)
example: "evt_draft_001"
industry:
type: string
description: 업종
enum:
- 음식점
- 카페
- 소매점
- 뷰티/미용
- 의료/헬스케어
- 기타
example: "음식점"
region:
type: string
description: 지역 (시/구/동)
example: "서울 강남구"
purpose:
type: string
description: 이벤트 목적
enum:
- 신규 고객 유치
- 재방문 유도
- 매출 증대
- 인지도 향상
example: "신규 고객 유치"
storeInfo:
$ref: "#/components/schemas/StoreInfo"
EventRecommendationRequest:
type: object
required:
- eventDraftId
- purpose
- industry
- region
- storeInfo
properties:
eventDraftId:
type: string
description: 이벤트 초안 ID
example: "evt_draft_001"
purpose:
type: string
description: 이벤트 목적
enum:
- 신규 고객 유치
- 재방문 유도
- 매출 증대
- 인지도 향상
example: "신규 고객 유치"
industry:
type: string
description: 업종
example: "음식점"
region:
type: string
description: 지역
example: "서울 강남구"
storeInfo:
$ref: "#/components/schemas/StoreInfo"
trendAnalysisJobId:
type: string
description: |
트렌드 분석 Job ID (선택)
- 제공 시 해당 트렌드 분석 결과 재사용
- 미제공 시 새로운 트렌드 분석 수행
example: "job_ai_20250122_001"
StoreInfo:
type: object
required:
- storeName
properties:
storeName:
type: string
description: 매장명
example: "맛있는 고깃집"
storeSize:
type: string
description: 매장 크기
enum:
- 소형
- 중형
- 대형
example: "중형"
monthlyRevenue:
type: integer
format: int64
description: 월 평균 매출 (원)
minimum: 0
example: 30000000
JobCreatedResponse:
type: object
required:
- jobId
- status
- message
properties:
jobId:
type: string
description: Job ID (폴링 조회용)
example: "job_ai_20250122_001"
status:
type: string
enum:
- PENDING
- PROCESSING
description: Job 상태
example: "PROCESSING"
message:
type: string
description: 상태 메시지
example: "트렌드 분석이 진행 중입니다"
estimatedCompletionTime:
type: string
format: date-time
description: 예상 완료 시간 (ISO 8601)
example: "2025-01-22T10:05:30Z"
TrendAnalysisJobResponse:
type: object
required:
- jobId
- status
- message
- createdAt
properties:
jobId:
type: string
description: Job ID
example: "job_ai_20250122_001"
status:
type: string
enum:
- PENDING
- PROCESSING
- COMPLETED
- FAILED
description: Job 상태
example: "COMPLETED"
message:
type: string
description: 상태 메시지
example: "트렌드 분석이 완료되었습니다"
progress:
type: integer
minimum: 0
maximum: 100
description: 진행률 (%)
example: 100
result:
$ref: "#/components/schemas/TrendAnalysisResult"
error:
$ref: "#/components/schemas/JobError"
createdAt:
type: string
format: date-time
description: Job 생성 시간
example: "2025-01-22T10:05:00Z"
estimatedCompletionTime:
type: string
format: date-time
description: 예상 완료 시간 (PROCESSING 상태일 때만)
example: "2025-01-22T10:05:30Z"
completedAt:
type: string
format: date-time
description: Job 완료 시간 (COMPLETED 상태일 때만)
example: "2025-01-22T10:05:25Z"
failedAt:
type: string
format: date-time
description: Job 실패 시간 (FAILED 상태일 때만)
example: "2025-01-22T10:05:45Z"
EventRecommendationJobResponse:
type: object
required:
- jobId
- status
- message
- createdAt
properties:
jobId:
type: string
description: Job ID
example: "job_ai_20250122_002"
status:
type: string
enum:
- PENDING
- PROCESSING
- COMPLETED
- FAILED
description: Job 상태
example: "COMPLETED"
message:
type: string
description: 상태 메시지
example: "이벤트 추천이 완료되었습니다"
progress:
type: integer
minimum: 0
maximum: 100
description: 진행률 (%)
example: 100
result:
$ref: "#/components/schemas/EventRecommendationResult"
error:
$ref: "#/components/schemas/JobError"
createdAt:
type: string
format: date-time
description: Job 생성 시간
example: "2025-01-22T10:05:00Z"
estimatedCompletionTime:
type: string
format: date-time
description: 예상 완료 시간 (PROCESSING 상태일 때만)
example: "2025-01-22T10:05:30Z"
completedAt:
type: string
format: date-time
description: Job 완료 시간 (COMPLETED 상태일 때만)
example: "2025-01-22T10:05:35Z"
failedAt:
type: string
format: date-time
description: Job 실패 시간 (FAILED 상태일 때만)
example: "2025-01-22T10:05:45Z"
TrendAnalysisResult:
type: object
required:
- industryTrends
- regionalCharacteristics
- seasonalPatterns
properties:
industryTrends:
$ref: "#/components/schemas/IndustryTrends"
regionalCharacteristics:
$ref: "#/components/schemas/RegionalCharacteristics"
seasonalPatterns:
$ref: "#/components/schemas/SeasonalPatterns"
IndustryTrends:
type: object
required:
- successfulEventTypes
- popularPrizes
- effectiveParticipationMethods
properties:
successfulEventTypes:
type: array
description: 최근 성공한 이벤트 유형 (최대 5개)
items:
type: object
required:
- type
- successRate
properties:
type:
type: string
description: 이벤트 유형
example: "할인 이벤트"
successRate:
type: integer
minimum: 0
maximum: 100
description: 성공률 (%)
example: 85
popularPrizes:
type: array
description: 고객 선호 경품 Top 5
items:
type: object
required:
- prize
- preferenceScore
properties:
prize:
type: string
description: 경품명
example: "커피 쿠폰"
preferenceScore:
type: integer
minimum: 0
maximum: 100
description: 선호도 점수
example: 92
effectiveParticipationMethods:
type: array
description: 효과적인 참여 방법
items:
type: object
required:
- method
- engagementRate
properties:
method:
type: string
description: 참여 방법
example: "간단한 설문조사"
engagementRate:
type: integer
minimum: 0
maximum: 100
description: 참여율 (%)
example: 75
RegionalCharacteristics:
type: object
required:
- successRate
- demographicProfile
properties:
successRate:
type: integer
minimum: 0
maximum: 100
description: 해당 지역 이벤트 성공률 (%)
example: 82
demographicProfile:
type: object
required:
- ageGroups
- genderDistribution
properties:
ageGroups:
type: array
description: 연령대별 분포
items:
type: object
required:
- range
- percentage
properties:
range:
type: string
description: 연령대
example: "20-29"
percentage:
type: integer
minimum: 0
maximum: 100
description: 비율 (%)
example: 35
genderDistribution:
type: object
required:
- male
- female
properties:
male:
type: integer
minimum: 0
maximum: 100
description: 남성 비율 (%)
example: 45
female:
type: integer
minimum: 0
maximum: 100
description: 여성 비율 (%)
example: 55
SeasonalPatterns:
type: object
required:
- currentSeason
- recommendedEventTypes
properties:
currentSeason:
type: string
description: 현재 계절
enum:
-
- 여름
- 가을
- 겨울
example: "겨울"
recommendedEventTypes:
type: array
description: 계절별 추천 이벤트 유형
items:
type: string
example:
- "따뜻한 음료 할인"
- "연말 감사 이벤트"
specialOccasions:
type: array
description: 다가오는 특별 이벤트 (명절, 기념일 등)
items:
type: object
required:
- occasion
- daysUntil
properties:
occasion:
type: string
description: 특별 이벤트명
example: "설날"
daysUntil:
type: integer
description: 남은 일수
example: 15
EventRecommendationResult:
type: object
required:
- recommendations
properties:
recommendations:
type: array
description: 3가지 이벤트 추천안
minItems: 3
maxItems: 3
items:
$ref: "#/components/schemas/EventRecommendation"
EventRecommendation:
type: object
required:
- option
- title
- budget
- prize
- participationMethod
- estimatedParticipants
- estimatedROI
- promotionalTexts
- hashtags
properties:
option:
type: integer
description: 옵션 번호 (1, 2, 3)
enum: [1, 2, 3]
example: 1
title:
type: string
description: 이벤트 제목 (수정 가능)
maxLength: 50
example: "신규 고객 환영 커피 쿠폰 증정"
budget:
type: string
description: 예산 수준
enum:
- low
- medium
- high
example: "low"
prize:
type: object
required:
- name
- quantity
- estimatedCost
properties:
name:
type: string
description: 경품명 (수정 가능)
example: "아메리카노 쿠폰"
quantity:
type: integer
minimum: 1
description: 경품 수량
example: 100
estimatedCost:
type: integer
minimum: 0
description: 예상 비용 (원)
example: 300000
participationMethod:
type: object
required:
- type
- difficulty
- description
properties:
type:
type: string
description: 참여 방법 유형
example: "간단한 설문조사"
difficulty:
type: string
description: 난이도
enum:
- low
- medium
- high
example: "low"
description:
type: string
description: 참여 방법 상세 설명
example: "매장 방문 후 QR 코드 스캔 및 간단한 설문"
estimatedParticipants:
type: integer
minimum: 0
description: 예상 참여자 수
example: 150
estimatedROI:
type: integer
minimum: 0
description: 예상 투자 대비 수익률 (%)
example: 250
promotionalTexts:
type: array
description: 홍보 문구 (5개)
minItems: 5
maxItems: 5
items:
type: string
example:
- "따뜻한 커피 한 잔으로 시작하는 하루!"
- "신규 방문 고객님께 특별한 선물"
- "강남 최고의 고깃집에서 만나요"
- "지금 바로 참여하세요!"
- "한정 수량! 서두르세요!"
hashtags:
type: array
description: SNS 해시태그 (자동 생성)
items:
type: string
example:
- "#강남맛집"
- "#커피쿠폰"
- "#신규고객환영"
JobError:
type: object
required:
- code
- detail
properties:
code:
type: string
description: 에러 코드
enum:
- AI_API_ERROR
- TIMEOUT
- CIRCUIT_BREAKER_OPEN
- INVALID_PARAMETERS
- INTERNAL_ERROR
example: "AI_API_ERROR"
detail:
type: string
description: 에러 상세 메시지
example: "External AI API timeout after 30 seconds"
ErrorResponse:
type: object
required:
- error
- message
- timestamp
properties:
error:
type: string
description: 에러 타입
example: "BAD_REQUEST"
message:
type: string
description: 에러 메시지
example: "필수 파라미터가 누락되었습니다"
details:
type: array
description: 상세 에러 정보
items:
type: object
properties:
field:
type: string
description: 에러 필드
example: "industry"
message:
type: string
description: 필드별 에러 메시지
example: "업종은 필수 입력 항목입니다"
timestamp:
type: string
format: date-time
description: 에러 발생 시간
example: "2025-01-22T10:05:00Z"
responses:
BadRequest:
description: 잘못된 요청
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
error: "BAD_REQUEST"
message: "필수 파라미터가 누락되었습니다"
details:
- field: "industry"
message: "업종은 필수 입력 항목입니다"
timestamp: "2025-01-22T10:05:00Z"
Unauthorized:
description: 인증 실패
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
error: "UNAUTHORIZED"
message: "유효하지 않은 인증 토큰입니다"
timestamp: "2025-01-22T10:05:00Z"
NotFound:
description: 리소스를 찾을 수 없음
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
error: "NOT_FOUND"
message: "Job을 찾을 수 없습니다"
timestamp: "2025-01-22T10:05:00Z"
TooManyRequests:
description: 요청 제한 초과
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
error: "TOO_MANY_REQUESTS"
message: "요청 제한을 초과했습니다. 잠시 후 다시 시도해주세요"
timestamp: "2025-01-22T10:05:00Z"
headers:
Retry-After:
description: 재시도 가능 시간 (초)
schema:
type: integer
example: 60
InternalServerError:
description: 서버 내부 오류
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
example:
error: "INTERNAL_SERVER_ERROR"
message: "서버 내부 오류가 발생했습니다"
timestamp: "2025-01-22T10:05:00Z"