@startuml ai-트렌드분석및추천 !theme mono title AI Service - 트렌드 분석 및 이벤트 추천 (내부 시퀀스) actor Client participant "Kafka Consumer" as Consumer <> participant "JobMessageHandler" as Handler <> participant "AIRecommendationService" as Service <> participant "TrendAnalysisEngine" as TrendEngine <> participant "RecommendationEngine" as RecommendEngine <> participant "CacheManager" as Cache <> participant "CircuitBreakerManager" as CB <> participant "ExternalAIClient" as AIClient <> participant "JobStateManager" as JobState <> participant "Redis" as Redis <> participant "Event DB" as EventDB <> participant "External AI API" as ExternalAPI <> participant "Kafka Producer" as Producer <> note over Consumer: Kafka ai-job Topic 구독\nConsumer Group: ai-service-group == 1. Job 메시지 수신 == Consumer -> Handler: onMessage(jobMessage)\n{jobId, eventDraftId, 목적, 업종, 지역, 매장정보} activate Handler Handler -> Handler: 메시지 유효성 검증 note right 검증 항목: - jobId 존재 여부 - eventDraftId 유효성 - 필수 파라미터 (목적, 업종, 지역) end note alt 유효하지 않은 메시지 Handler -> Producer: DLQ 발행 (Dead Letter Queue)\n{jobId, error: INVALID_MESSAGE} Handler --> Consumer: ACK (메시지 처리 완료) note over Handler: 잘못된 메시지는 DLQ로 이동\n수동 검토 필요 else 유효한 메시지 Handler -> JobState: updateJobStatus(jobId, PROCESSING) JobState -> Redis: SET job:{jobId}:status = PROCESSING Redis --> JobState: OK JobState --> Handler: 상태 업데이트 완료 Handler -> Service: generateRecommendations(\neventDraftId, 목적, 업종, 지역, 매장정보) activate Service == 2. 트렌드 분석 == Service -> TrendEngine: analyzeTrends(업종, 지역, 목적) activate TrendEngine TrendEngine -> Cache: getCachedTrend(업종, 지역) Cache -> Redis: GET trend:{업종}:{지역} Redis --> Cache: 캐시 결과 alt 캐시 히트 Cache --> TrendEngine: 캐시된 트렌드 데이터 note right 캐시 키: trend:{업종}:{지역} TTL: 1시간 데이터: { industry_trends, regional_characteristics, seasonal_patterns } end note else 캐시 미스 TrendEngine -> EventDB: 과거 이벤트 데이터 조회\n(업종과 지역으로 필터링,\n최근 3개월 이벤트,\nROI 내림차순 정렬) EventDB --> TrendEngine: 이벤트 통계 데이터\n{성공 이벤트 리스트, ROI 정보} TrendEngine -> TrendEngine: 트렌드 패턴 분석 note right 분석 항목: 1. 업종 트렌드 - 최근 3개월 성공 이벤트 유형 - 고객 선호 경품 Top 5 - 효과적인 참여 방법 2. 지역 특성 - 해당 지역 이벤트 성공률 - 지역 고객 연령대/성별 분포 3. 시즌 특성 - 계절별 추천 이벤트 - 특별 시즌 (명절, 기념일) end note TrendEngine -> CB: executeWithCircuitBreaker(\nAI API 트렌드 분석 호출) activate CB CB -> CB: Circuit Breaker 상태 확인 note right Circuit Breaker 설정: - Failure Rate Threshold: 50% - Timeout: 30초 - Half-Open Wait Duration: 30초 - Permitted Calls in Half-Open: 3 end note alt Circuit CLOSED (정상) CB -> AIClient: callAIAPI(\nmethod: "trendAnalysis",\nprompt: 트렌드 분석 프롬프트,\ntimeout: 30초) activate AIClient AIClient -> AIClient: 프롬프트 구성 note right 프롬프트 예시: "당신은 마케팅 트렌드 분석 전문가입니다. 업종: {업종} 지역: {지역} 과거 데이터: {이벤트 통계} 다음을 분석하세요: 1. 업종 트렌드 (성공 이벤트 유형) 2. 지역 특성 (고객 특성) 3. 시즌 특성 (현재 시기 추천)" end note AIClient -> ExternalAPI: POST /api/v1/analyze\nAuthorization: Bearer {API_KEY}\nTimeout: 30초 activate ExternalAPI ExternalAPI --> AIClient: 200 OK\n{트렌드 분석 결과} deactivate ExternalAPI AIClient -> AIClient: 응답 검증 및 파싱 AIClient --> CB: 분석 결과 deactivate AIClient CB -> CB: 성공 기록 (Circuit Breaker) CB --> TrendEngine: 트렌드 분석 결과 deactivate CB TrendEngine -> Cache: cacheTrend(\nkey: trend:{업종}:{지역},\ndata: 분석결과,\nTTL: 1시간) Cache -> Redis: SETEX trend:{업종}:{지역} 3600 {분석결과} Redis --> Cache: OK Cache --> TrendEngine: 캐싱 완료 else Circuit OPEN (장애) CB --> TrendEngine: CircuitBreakerOpenException TrendEngine -> TrendEngine: Fallback 실행\n(기본 트렌드 데이터 사용) note right Fallback 전략: - 이전 캐시 데이터 반환 - 또는 기본 트렌드 템플릿 사용 - 클라이언트에 안내 메시지 포함 end note else Circuit HALF-OPEN (복구 시도) CB -> AIClient: 제한된 요청 허용 (3개) AIClient -> ExternalAPI: POST /api/v1/analyze ExternalAPI --> AIClient: 200 OK AIClient --> CB: 성공 CB -> CB: 연속 성공 시 CLOSED로 전환 CB --> TrendEngine: 트렌드 분석 결과 else Timeout (30초 초과) CB --> TrendEngine: TimeoutException TrendEngine -> TrendEngine: Fallback 실행 end end TrendEngine --> Service: 트렌드 분석 완료\n{업종트렌드, 지역특성, 시즌특성} deactivate TrendEngine == 3. 이벤트 추천 생성 (3가지 옵션) == Service -> RecommendEngine: generateRecommendations(\n목적, 트렌드, 매장정보) activate RecommendEngine RecommendEngine -> RecommendEngine: 추천 컨텍스트 구성 note right 추천 입력: - 이벤트 목적 (신규 고객 유치 등) - 트렌드 분석 결과 - 매장 정보 (업종, 위치, 크기) - 예산 범위 (저/중/고) end note group parallel RecommendEngine -> CB: executeWithCircuitBreaker(\nAI API 추천 생성 - 옵션 1: 저비용) activate CB CB -> AIClient: callAIAPI(\nmethod: "generateRecommendation",\nprompt: 저비용 추천 프롬프트,\ntimeout: 10초) activate AIClient AIClient -> AIClient: 프롬프트 구성 note right 옵션 1 프롬프트: "저비용, 높은 참여율 중심 이벤트 기획 목적: {목적} 트렌드: {트렌드} 매장: {매장정보} 출력 형식: - 이벤트 제목 - 추천 경품 (예산: 저) - 참여 방법 (난이도: 낮음) - 예상 참여자 수 - 예상 비용 - 예상 ROI" end note AIClient -> ExternalAPI: POST /api/v1/recommend\n(저비용 옵션) ExternalAPI --> AIClient: 200 OK\n{추천안 1} AIClient --> CB: 추천안 1 deactivate AIClient CB --> RecommendEngine: 옵션 1 완료 deactivate CB RecommendEngine -> CB: executeWithCircuitBreaker(\nAI API 추천 생성 - 옵션 2: 중비용) activate CB CB -> AIClient: callAIAPI(\nmethod: "generateRecommendation",\nprompt: 중비용 추천 프롬프트,\ntimeout: 10초) activate AIClient AIClient -> AIClient: 프롬프트 구성 note right 옵션 2 프롬프트: "중비용, 균형잡힌 ROI 이벤트 기획 목적: {목적} 트렌드: {트렌드} 매장: {매장정보} 출력 형식: - 이벤트 제목 - 추천 경품 (예산: 중) - 참여 방법 (난이도: 중간) - 예상 참여자 수 - 예상 비용 - 예상 ROI" end note AIClient -> ExternalAPI: POST /api/v1/recommend\n(중비용 옵션) ExternalAPI --> AIClient: 200 OK\n{추천안 2} AIClient --> CB: 추천안 2 deactivate AIClient CB --> RecommendEngine: 옵션 2 완료 deactivate CB RecommendEngine -> CB: executeWithCircuitBreaker(\nAI API 추천 생성 - 옵션 3: 고비용) activate CB CB -> AIClient: callAIAPI(\nmethod: "generateRecommendation",\nprompt: 고비용 추천 프롬프트,\ntimeout: 10초) activate AIClient AIClient -> AIClient: 프롬프트 구성 note right 옵션 3 프롬프트: "고비용, 높은 매출 증대 이벤트 기획 목적: {목적} 트렌드: {트렌드} 매장: {매장정보} 출력 형식: - 이벤트 제목 - 추천 경품 (예산: 고) - 참여 방법 (난이도: 높음) - 예상 참여자 수 - 예상 비용 - 예상 ROI" end note AIClient -> ExternalAPI: POST /api/v1/recommend\n(고비용 옵션) ExternalAPI --> AIClient: 200 OK\n{추천안 3} AIClient --> CB: 추천안 3 deactivate AIClient CB --> RecommendEngine: 옵션 3 완료 deactivate CB end RecommendEngine -> RecommendEngine: 3가지 추천안 통합 및 검증 note right 검증 항목: - 필수 필드 존재 여부 - 예상 성과 계산 (ROI) - 추천안 차별화 확인 - 홍보 문구 생성 (각 5개) - SNS 해시태그 자동 생성 end note RecommendEngine --> Service: 3가지 추천안 생성 완료 deactivate RecommendEngine == 4. 결과 저장 및 Job 상태 업데이트 == Service -> Cache: cacheRecommendations(\nkey: ai:recommendation:{eventDraftId},\ndata: {트렌드+추천안},\nTTL: 24시간) Cache -> Redis: SETEX ai:recommendation:{eventDraftId} 86400 {결과} Redis --> Cache: OK Cache --> Service: 캐싱 완료 Service -> JobState: updateJobStatus(\njobId,\nstatus: COMPLETED,\nresult: {트렌드, 추천안}) JobState -> Redis: HSET job:{jobId} status COMPLETED result {JSON} Redis --> JobState: OK JobState --> Service: 상태 업데이트 완료 Service --> Handler: 추천 생성 완료\n{트렌드분석, 3가지추천안} deactivate Service Handler --> Consumer: ACK (메시지 처리 완료) deactivate Handler note over Consumer: Job 처리 완료\nRedis에 저장된 결과를\n클라이언트는 폴링으로 조회 end == 예외 처리 == note over Handler, Producer 1. AI API 장애 시: - Circuit Breaker Open - Fallback: 기본 트렌드 데이터 사용 - Job 상태: COMPLETED (안내 메시지 포함) 2. Timeout (30초 초과): - Circuit Breaker로 즉시 실패 - Retry 없음 (비동기 Job) - Job 상태: FAILED 3. Kafka 메시지 처리 실패: - DLQ로 이동 - 수동 검토 및 재처리 4. Redis 장애: - 캐싱 스킵, DB만 사용 - Job 상태는 메모리에 임시 저장 end note @enduml