kt-event-marketing/develop/dev/ai-service-workflow.md
박세원 c6b33885e0 AI Service Security 설정 단순화 및 워크플로우 문서 추가
- SecurityConfig CORS 설정 제거 및 단순화
- 모든 요청 허용으로 변경 (내부 API 특성 반영)
- DevTools 요청 정적 리소스 제외 처리
- AI Service 워크플로우 문서 추가

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 16:44:23 +09:00

13 KiB

AI Service 전체 메서드 워크플로우

1. AI 추천 생성 워크플로우 (Kafka 기반 비동기)

sequenceDiagram
    participant ES as Event Service
    participant Kafka as Kafka Topic
    participant Consumer as AIJobConsumer
    participant ARS as AIRecommendationService
    participant JSS as JobStatusService
    participant TAS as TrendAnalysisService
    participant CS as CacheService
    participant CAC as ClaudeApiClient
    participant Claude as Claude API
    participant Redis as Redis

    %% 1. Event Service가 Kafka 메시지 발행
    ES->>Kafka: Publish AIJobMessage<br/>(ai-event-generation-job topic)

    %% 2. Kafka Consumer가 메시지 수신
    Kafka->>Consumer: consume(AIJobMessage)
    Note over Consumer: @KafkaListener<br/>groupId: ai-service-consumers

    %% 3. AI 추천 생성 시작
    Consumer->>ARS: generateRecommendations(message)
    activate ARS

    %% 4. Job 상태: PROCESSING (10%)
    ARS->>JSS: updateJobStatus(jobId, PROCESSING, "트렌드 분석 중")
    JSS->>CS: saveJobStatus(jobId, status)
    CS->>Redis: SET ai:job:status:{jobId}

    %% 5. 트렌드 분석
    ARS->>ARS: analyzeTrend(message)
    ARS->>CS: getTrend(industry, region)
    CS->>Redis: GET ai:trend:{industry}:{region}

    alt 캐시 HIT
        Redis-->>CS: TrendAnalysis (cached)
        CS-->>ARS: TrendAnalysis
    else 캐시 MISS
        ARS->>TAS: analyzeTrend(industry, region)
        activate TAS

        %% Circuit Breaker 적용
        TAS->>TAS: circuitBreakerManager.executeWithCircuitBreaker()
        TAS->>CAC: sendMessage(apiKey, version, request)
        CAC->>Claude: POST /v1/messages
        Note over Claude: Model: claude-sonnet-4-5<br/>System: "트렌드 분석 전문가"<br/>Prompt: 업종/지역/계절 트렌드
        Claude-->>CAC: ClaudeResponse
        CAC-->>TAS: ClaudeResponse

        TAS->>TAS: parseResponse(responseText)
        TAS-->>ARS: TrendAnalysis
        deactivate TAS

        %% 트렌드 캐싱
        ARS->>CS: saveTrend(industry, region, analysis)
        CS->>Redis: SET ai:trend:{industry}:{region} (TTL: 1시간)
    end

    %% 6. Job 상태: PROCESSING (50%)
    ARS->>JSS: updateJobStatus(jobId, PROCESSING, "이벤트 추천안 생성 중")
    JSS->>CS: saveJobStatus(jobId, status)
    CS->>Redis: SET ai:job:status:{jobId}

    %% 7. 이벤트 추천안 생성
    ARS->>ARS: createRecommendations(message, trendAnalysis)
    ARS->>ARS: circuitBreakerManager.executeWithCircuitBreaker()
    ARS->>CAC: sendMessage(apiKey, version, request)
    CAC->>Claude: POST /v1/messages
    Note over Claude: Model: claude-sonnet-4-5<br/>System: "이벤트 기획 전문가"<br/>Prompt: 3가지 추천안 생성
    Claude-->>CAC: ClaudeResponse
    CAC-->>ARS: ClaudeResponse

    ARS->>ARS: parseRecommendationResponse(responseText)

    %% 8. Job 상태: PROCESSING (90%)
    ARS->>JSS: updateJobStatus(jobId, PROCESSING, "결과 저장 중")
    JSS->>CS: saveJobStatus(jobId, status)
    CS->>Redis: SET ai:job:status:{jobId}

    %% 9. 결과 저장
    ARS->>CS: saveRecommendation(eventId, result)
    CS->>Redis: SET ai:recommendation:{eventId} (TTL: 24시간)

    %% 10. Job 상태: COMPLETED (100%)
    ARS->>JSS: updateJobStatus(jobId, COMPLETED, "AI 추천 완료")
    JSS->>CS: saveJobStatus(jobId, status)
    CS->>Redis: SET ai:job:status:{jobId}

    deactivate ARS

    %% 11. Kafka ACK
    Consumer->>Kafka: acknowledgment.acknowledge()

2. Job 상태 조회 워크플로우 (동기)

sequenceDiagram
    participant ES as Event Service
    participant Controller as InternalJobController
    participant JSS as JobStatusService
    participant CS as CacheService
    participant Redis as Redis

    %% 1. Event Service가 Job 상태 조회
    ES->>Controller: GET /api/v1/ai-service/internal/jobs/{jobId}/status

    %% 2. Job 상태 조회
    Controller->>JSS: getJobStatus(jobId)
    activate JSS

    JSS->>CS: getJobStatus(jobId)
    CS->>Redis: GET ai:job:status:{jobId}

    alt 상태 존재
        Redis-->>CS: JobStatusResponse
        CS-->>JSS: Object (JobStatusResponse)
        JSS->>JSS: objectMapper.convertValue()
        JSS-->>Controller: JobStatusResponse
        Controller-->>ES: 200 OK + JobStatusResponse
    else 상태 없음
        Redis-->>CS: null
        CS-->>JSS: null
        JSS-->>Controller: JobNotFoundException
        Controller-->>ES: 404 Not Found
    end

    deactivate JSS

3. AI 추천 결과 조회 워크플로우 (동기)

sequenceDiagram
    participant ES as Event Service
    participant Controller as InternalRecommendationController
    participant ARS as AIRecommendationService
    participant CS as CacheService
    participant Redis as Redis

    %% 1. Event Service가 AI 추천 결과 조회
    ES->>Controller: GET /api/v1/ai-service/internal/recommendations/{eventId}

    %% 2. 추천 결과 조회
    Controller->>ARS: getRecommendation(eventId)
    activate ARS

    ARS->>CS: getRecommendation(eventId)
    CS->>Redis: GET ai:recommendation:{eventId}

    alt 결과 존재
        Redis-->>CS: AIRecommendationResult
        CS-->>ARS: Object (AIRecommendationResult)
        ARS->>ARS: objectMapper.convertValue()
        ARS-->>Controller: AIRecommendationResult
        Controller-->>ES: 200 OK + AIRecommendationResult
    else 결과 없음
        Redis-->>CS: null
        CS-->>ARS: null
        ARS-->>Controller: RecommendationNotFoundException
        Controller-->>ES: 404 Not Found
    end

    deactivate ARS

4. 헬스체크 워크플로우 (동기)

sequenceDiagram
    participant Client as Client/Actuator
    participant Controller as HealthController
    participant Redis as Redis

    %% 1. 헬스체크 요청
    Client->>Controller: GET /api/v1/ai-service/health

    %% 2. Redis 상태 확인
    Controller->>Controller: checkRedis()

    alt RedisTemplate 존재
        Controller->>Redis: PING
        alt Redis 정상
            Redis-->>Controller: PONG
            Controller->>Controller: redisStatus = UP
        else Redis 오류
            Redis-->>Controller: Exception
            Controller->>Controller: redisStatus = DOWN
        end
    else RedisTemplate 없음
        Controller->>Controller: redisStatus = UNKNOWN
    end

    %% 3. 전체 상태 판단
    alt Redis DOWN
        Controller->>Controller: overallStatus = DEGRADED
    else Redis UP/UNKNOWN
        Controller->>Controller: overallStatus = UP
    end

    %% 4. 응답
    Controller-->>Client: 200 OK + HealthCheckResponse

5. 주요 컴포넌트 메서드 목록

5.1 Controller Layer

InternalJobController

메서드 HTTP 엔드포인트 설명
getJobStatus(jobId) GET /api/v1/ai-service/internal/jobs/{jobId}/status Job 상태 조회
createTestJob(jobId) GET /api/v1/ai-service/internal/jobs/debug/create-test-job/{jobId} 테스트 Job 생성 (디버그)

InternalRecommendationController

메서드 HTTP 엔드포인트 설명
getRecommendation(eventId) GET /api/v1/ai-service/internal/recommendations/{eventId} AI 추천 결과 조회
debugRedisKeys() GET /api/v1/ai-service/internal/recommendations/debug/redis-keys Redis 모든 키 조회
debugRedisKey(key) GET /api/v1/ai-service/internal/recommendations/debug/redis-key/{key} Redis 특정 키 조회
searchAllDatabases() GET /api/v1/ai-service/internal/recommendations/debug/search-all-databases 전체 DB 검색
createTestData(eventId) GET /api/v1/ai-service/internal/recommendations/debug/create-test-data/{eventId} 테스트 데이터 생성

HealthController

메서드 HTTP 엔드포인트 설명
healthCheck() GET /api/v1/ai-service/health 서비스 헬스체크
checkRedis() - (내부) Redis 연결 확인

5.2 Service Layer

AIRecommendationService

메서드 호출자 설명
getRecommendation(eventId) Controller Redis에서 추천 결과 조회
generateRecommendations(message) AIJobConsumer AI 추천 생성 (전체 프로세스)
analyzeTrend(message) 내부 트렌드 분석 (캐시 확인 포함)
createRecommendations(message, trendAnalysis) 내부 이벤트 추천안 생성
callClaudeApiForRecommendations(message, trendAnalysis) 내부 Claude API 호출 (추천안)
buildRecommendationPrompt(message, trendAnalysis) 내부 추천안 프롬프트 생성
parseRecommendationResponse(responseText) 내부 추천안 응답 파싱
parseEventRecommendation(node) 내부 EventRecommendation 파싱
parseRange(node) 내부 Range 객체 파싱
extractJsonFromMarkdown(text) 내부 Markdown에서 JSON 추출

TrendAnalysisService

메서드 호출자 설명
analyzeTrend(industry, region) AIRecommendationService 트렌드 분석 수행
callClaudeApi(industry, region) 내부 Claude API 호출 (트렌드)
buildPrompt(industry, region) 내부 트렌드 분석 프롬프트 생성
parseResponse(responseText) 내부 트렌드 응답 파싱
extractJsonFromMarkdown(text) 내부 Markdown에서 JSON 추출
parseTrendKeywords(arrayNode) 내부 TrendKeyword 리스트 파싱

JobStatusService

메서드 호출자 설명
getJobStatus(jobId) Controller Job 상태 조회
updateJobStatus(jobId, status, message) AIRecommendationService Job 상태 업데이트
calculateProgress(status) 내부 상태별 진행률 계산

CacheService

메서드 호출자 설명
set(key, value, ttlSeconds) 내부 범용 캐시 저장
get(key) 내부 범용 캐시 조회
delete(key) 외부 캐시 삭제
saveJobStatus(jobId, status) JobStatusService Job 상태 저장
getJobStatus(jobId) JobStatusService Job 상태 조회
saveRecommendation(eventId, recommendation) AIRecommendationService AI 추천 결과 저장
getRecommendation(eventId) AIRecommendationService AI 추천 결과 조회
saveTrend(industry, region, trend) AIRecommendationService 트렌드 분석 결과 저장
getTrend(industry, region) AIRecommendationService 트렌드 분석 결과 조회

5.3 Consumer Layer

AIJobConsumer

메서드 트리거 설명
consume(message, topic, offset, ack) Kafka Message Kafka 메시지 수신 및 처리

5.4 Client Layer

ClaudeApiClient (Feign)

메서드 호출자 설명
sendMessage(apiKey, anthropicVersion, request) TrendAnalysisService, AIRecommendationService Claude API 호출

6. Redis 캐시 키 구조

키 패턴 설명 TTL
ai:job:status:{jobId} Job 상태 정보 24시간 (86400초)
ai:recommendation:{eventId} AI 추천 결과 24시간 (86400초)
ai:trend:{industry}:{region} 트렌드 분석 결과 1시간 (3600초)

7. Claude API 호출 정보

7.1 트렌드 분석

  • URL: https://api.anthropic.com/v1/messages
  • Model: claude-sonnet-4-5-20250929
  • Max Tokens: 4096
  • Temperature: 0.7
  • System Prompt: "당신은 마케팅 트렌드 분석 전문가입니다. 업종별, 지역별 트렌드를 분석하고 인사이트를 제공합니다."
  • 응답 형식: JSON (industryTrends, regionalTrends, seasonalTrends)

7.2 이벤트 추천안 생성

  • URL: https://api.anthropic.com/v1/messages
  • Model: claude-sonnet-4-5-20250929
  • Max Tokens: 4096
  • Temperature: 0.7
  • System Prompt: "당신은 소상공인을 위한 마케팅 이벤트 기획 전문가입니다. 트렌드 분석을 바탕으로 실행 가능한 이벤트 추천안을 제공합니다."
  • 응답 형식: JSON (recommendations: 3가지 옵션)

8. Circuit Breaker 설정

적용 대상

  • claudeApi: 모든 Claude API 호출

설정값

failure-rate-threshold: 50%
slow-call-duration-threshold: 60초
sliding-window-size: 10
minimum-number-of-calls: 5
wait-duration-in-open-state: 60초
timeout-duration: 300초 (5분)

Fallback 메서드

  • AIServiceFallback.getDefaultTrendAnalysis(): 기본 트렌드 분석
  • AIServiceFallback.getDefaultRecommendations(): 기본 추천안

9. 에러 처리

Exception 종류

Exception HTTP Code 발생 조건
RecommendationNotFoundException 404 Redis에 추천 결과 없음
JobNotFoundException 404 Redis에 Job 상태 없음
AIServiceException 500 AI 서비스 내부 오류

에러 응답 예시

{
  "timestamp": "2025-10-30T15:30:00",
  "status": 404,
  "error": "Not Found",
  "message": "추천 결과를 찾을 수 없습니다: eventId=evt-123",
  "path": "/api/v1/ai-service/internal/recommendations/evt-123"
}

10. 로깅 레벨

com.kt.ai: DEBUG
org.springframework.kafka: INFO
org.springframework.data.redis: INFO
io.github.resilience4j: DEBUG