@startuml analytics-대시보드조회 !theme mono title Analytics Service - 대시보드 조회 내부 시퀀스\n(UFR-ANAL-010: 실시간 성과분석 대시보드 조회) participant "AnalyticsController" as Controller participant "AnalyticsService" as Service participant "CacheService" as Cache participant "AnalyticsRepository" as Repository participant "ExternalChannelService" as ChannelService participant "ROICalculator" as Calculator participant "CircuitBreaker" as CB participant "Redis<>" as Redis database "Analytics DB<>" as DB -> Controller: GET /api/events/{id}/analytics\n+ Authorization: Bearer {token} activate Controller Controller -> Service: getDashboardData(eventId, userId) activate Service note right of Service **입력 검증** - eventId: UUID 형식 검증 - userId: JWT에서 추출 - 권한 확인: 매장 소유자 여부 end note Service -> Cache: get("analytics:dashboard:{eventId}") activate Cache note right of Cache **Cache-Aside 패턴** - Redis GET 호출 - Cache Key 구조: analytics:dashboard:{eventId} - TTL: 3600초 (1시간) end note Cache -> Redis: GET analytics:dashboard:{eventId} activate Redis alt Cache HIT Redis --> Cache: **Cache HIT**\n캐시된 데이터 반환\n{\n totalParticipants: 1234,\n totalViews: 17200,\n roi: 250,\n channelStats: [...],\n lastUpdated: "2025-10-22T10:30:00Z"\n} deactivate Redis Cache --> Service: Dashboard 데이터 (JSON) deactivate Cache note right of Service **응답 데이터 구조** - 4개 요약 카드 * 총 참여자 수, 달성률 * 총 노출 수, 증감률 * 예상 ROI, 업종 평균 대비 * 매출 증가율 - 채널별 성과 - 시간대별 참여 추이 - 참여자 프로필 분석 - 비교 분석 (업종 평균, 이전 이벤트) end note Service --> Controller: DashboardResponse\n(200 OK) deactivate Service Controller --> : 200 OK\nDashboard Data (JSON) deactivate Controller note over Controller, Redis **Cache HIT 시나리오 성능** - 응답 시간: 약 0.5초 - Redis 조회 시간: 0.01초 - 직렬화/역직렬화: 0.05초 - HTTP 오버헤드: 0.44초 - 예상 히트율: 95% end note else Cache MISS Redis --> Cache: **Cache MISS** (null) deactivate Redis Cache --> Service: null (캐시 미스) deactivate Cache note right of Service **Cache MISS 처리** - 데이터 통합 작업 시작 - 로컬 DB 조회 + 외부 API 병렬 호출 end note ||| == 1. Analytics DB 조회 (로컬 데이터) == Service -> Repository: getEventStats(eventId) activate Repository Repository -> DB: 이벤트 통계 조회\n(이벤트ID로 통계 데이터 조회) activate DB DB --> Repository: EventStatsEntity\n- totalParticipants\n- estimatedROI\n- salesGrowthRate deactivate DB Repository --> Service: EventStats deactivate Repository note right of Service **로컬 데이터 확보** - 총 참여자 수 - 예상 ROI (DB 캐시) - 매출 증가율 (POS 연동) end note ||| == 2. 외부 채널 API 병렬 호출 (Circuit Breaker 적용) == note right of Service **병렬 처리 시작** - CompletableFuture 4개 생성 - 우리동네TV, 지니TV, 링고비즈, SNS APIs 동시 호출 - Circuit Breaker 적용 (채널별 독립) end note par 외부 API 병렬 호출 Service -> ChannelService: getWooriTVStats(eventId) activate ChannelService ChannelService -> CB: execute("wooriTV", () -> callAPI()) activate CB note right of CB **Circuit Breaker** - State: CLOSED (정상) - Failure Rate: 50% 초과 시 OPEN - Timeout: 10초 end note CB -> CB: 외부 API 호출\nGET /stats/{eventId} alt Circuit Breaker CLOSED (정상) CB --> ChannelService: ChannelStats\n- views: 5000\n- clicks: 1200 deactivate CB ChannelService --> Service: WooriTVStats deactivate ChannelService else Circuit Breaker OPEN (장애) CB -> CB: **Fallback 실행**\n캐시된 이전 데이터 반환 note right of CB Fallback 전략: - Redis에서 이전 통계 조회 - 없으면 기본값 (0) 반환 - 알림: "일부 채널 데이터 로딩 실패" end note CB --> ChannelService: Fallback 데이터 deactivate CB ChannelService --> Service: WooriTVStats (Fallback) deactivate ChannelService end else Service -> ChannelService: getGenieTVStats(eventId) activate ChannelService ChannelService -> CB: execute("genieTV", () -> callAPI()) activate CB CB -> CB: 외부 API 호출\nGET /campaign/{id}/stats alt 정상 응답 CB --> ChannelService: ChannelStats\n- adViews: 10000\n- clicks: 500 deactivate CB ChannelService --> Service: GenieTVStats deactivate ChannelService else Timeout (10초 초과) CB -> CB: **Timeout 처리**\n기본값 반환 note right of CB Timeout 발생: - 리소스 점유 방지 - Fallback으로 기본값 (0) 설정 - 알림: "지니TV 데이터 로딩 지연" end note CB --> ChannelService: 기본값 (0) deactivate CB ChannelService --> Service: GenieTVStats (기본값) deactivate ChannelService end else Service -> ChannelService: getRingoBizStats(eventId) activate ChannelService ChannelService -> CB: execute("ringoBiz", () -> callAPI()) activate CB note right of CB **Circuit Breaker** - State: CLOSED (정상) - Failure Rate: 50% 초과 시 OPEN - Timeout: 10초 end note CB -> CB: 외부 API 호출\nGET /voice-stats/{eventId} alt 정상 응답 CB --> ChannelService: ChannelStats\n- calls: 3000\n- completed: 2500\n- avgDuration: 45초 deactivate CB ChannelService --> Service: RingoBizStats deactivate ChannelService else Timeout 또는 장애 CB -> CB: **Fallback 실행**\n기본값 반환 note right of CB 링고비즈 API 장애: - 기본값 (0) 반환 - 알림: "링고비즈 데이터 로딩 실패" end note CB --> ChannelService: 기본값 (0) deactivate CB ChannelService --> Service: RingoBizStats (기본값) deactivate ChannelService end else Service -> ChannelService: getSNSStats(eventId) activate ChannelService ChannelService -> CB: execute("SNS", () -> callAPIs()) activate CB note right of CB **SNS APIs 통합 호출** - Instagram API - Naver Blog API - Kakao Channel API - 3개 API 병렬 호출 end note CB -> CB: 외부 APIs 호출\n(Instagram, Naver, Kakao) alt 정상 응답 CB --> ChannelService: SNSStats\n- Instagram: likes 300, comments 50\n- Naver: views 2000\n- Kakao: shares 100 deactivate CB ChannelService --> Service: SNSStats deactivate ChannelService else 장애 또는 Timeout CB -> CB: **Fallback 실행**\n기본값 반환 note right of CB SNS API 장애: - 기본값 (0) 반환 - 알림: "SNS 데이터 로딩 실패" end note CB --> ChannelService: 기본값 (0) deactivate CB ChannelService --> Service: SNSStats (기본값) deactivate ChannelService end end ||| == 3. 데이터 통합 및 ROI 계산 == Service -> Service: mergeChannelStats(\n wooriTV, genieTV, ringoBiz, sns\n) note right of Service **데이터 통합** - 총 노출 수 = 외부 채널 노출 합계 - 총 참여자 수 = Analytics DB - 채널별 전환율 = 참여자 수 / 노출 수 - 링고비즈: 통화 완료 수 포함 end note Service -> Calculator: calculateROI(\n eventStats, channelStats\n) activate Calculator note right of Calculator **ROI 계산 로직** 총 비용 = 경품 비용 + 플랫폼 비용 예상 수익 = 매출 증가액 + 신규 고객 LTV ROI = (수익 - 비용) / 비용 × 100 end note Calculator --> Service: ROIData\n- roi: 250%\n- totalCost: 100만원\n- totalRevenue: 350만원\n- breakEvenPoint: 달성 deactivate Calculator Service -> Service: buildDashboardData(\n eventStats, channelStats, roiData\n) note right of Service **대시보드 데이터 구조 생성** - 4개 요약 카드 - 채널별 성과 차트 데이터 - 시간대별 참여 추이 - 참여자 프로필 분석 - 비교 분석 (업종 평균, 이전 이벤트) end note ||| == 4. Redis 캐싱 및 응답 == Service -> Cache: set(\n "analytics:dashboard:{eventId}",\n dashboardData,\n TTL=3600\n) activate Cache Cache -> Redis: 캐시 저장\nSET analytics:dashboard:{eventId}\nvalue={통합 데이터}\nEX 3600 (1시간) activate Redis Redis --> Cache: OK (저장 완료) deactivate Redis Cache --> Service: OK (캐싱 완료) deactivate Cache note right of Service **캐싱 완료** - TTL: 3600초 (1시간) - 다음 조회 시 Cache HIT - 예상 크기: 5KB - 갱신 주기: 1시간마다 새 데이터 조회 end note Service --> Controller: DashboardResponse\n(200 OK) deactivate Service Controller --> : 200 OK\nDashboard Data (JSON) deactivate Controller note over Controller, DB **Cache MISS 시나리오 성능** - 응답 시간: 약 3초 - Analytics DB 조회: 0.1초 - 외부 API 병렬 호출: 2초 (병렬 처리) - ROI 계산: 0.05초 - Redis 캐싱: 0.01초 - 직렬화/HTTP: 0.84초 end note end @enduml