STK11-03 자정 스케줄러·수동 갱신 트리거

작업 내용 (설계 의도)

변경 사항

매일 00:00 KST에 관심종목·추천을 ML fresh 계산 후 스냅샷에 적재하는 writer를 신설한다. 자동 스케줄러와 수동 트리거 두 진입점이 동일 RefreshSnapshotsUseCase를 호출한다. (선행: STK11-01 SnapshotGateway, STK11-02 getSignal(refresh))

  • domain: WatchlistGateway 인터페이스 + BackendWatchlistGatewayImpl — backend GET /api/v1/watchlist에서 적재 대상 symbol 목록 추출
  • application: RefreshSnapshotsUseCaseWatchlistGateway.findSymbols() 순회하며 SignalDomainService.getSignal(symbol, refresh=true)로 ML 계산·적재, 이어서 getRecommendations(refresh=true) 1회. 종목별 실패는 격리(한 종목 실패 시 나머지 계속), 결과 요약 반환
  • presentation: SnapshotRefreshScheduler@Scheduled(cron="0 0 0 * * *", zone="Asia/Seoul"), 활성/비활성 플래그(aggregator.snapshot.refresh.enabled)
  • presentation: SnapshotRefreshApiControllerPOST /api/v1/snapshots/refresh 수동 트리거 (데이터 누락·즉시 갱신용, 멱등 upsert라 중복 안전)

모니터링 (TDD Observability)

  • 적재 시작/종료/부분실패 종목 로그, ML 호출 시점(claude 비용 추적)
  • 메트릭: aggregator.snapshot.refresh.duration, aggregator.snapshot.refresh.failure{symbol}
  • 전체 실패 시 Discord 알림

롤백

aggregator.snapshot.refresh.enabled=false로 즉시 비활성화, 또는 aggregator 이전 버전 재배포.

다이어그램

처리 흐름

sequenceDiagram
    participant Entry as Scheduler / ManualTrigger
    participant UC as RefreshSnapshotsUseCase
    participant WL as WatchlistGateway
    participant DS as SignalDomainService
    Entry->>UC: execute()
    UC->>WL: findSymbols()
    WL-->>UC: symbols
    loop 종목별 (실패 격리)
        UC->>DS: getSignal(symbol, refresh=true)
    end
    UC->>DS: getRecommendations(limit, refresh=true)
    UC-->>Entry: RefreshSummary

클래스 의존

flowchart LR
    Sched[SnapshotRefreshScheduler] --> UC[RefreshSnapshotsUseCase]
    Ctrl[SnapshotRefreshApiController] --> UC
    UC --> WL[WatchlistGateway]
    UC --> DS[SignalDomainService]

테스트 케이스

  • 스케줄러가 cron 발화 시 RefreshSnapshotsUseCase를 1회 호출한다
  • 수동 트리거 POST /api/v1/snapshots/refresh가 동일 UseCase를 호출한다
  • 관심종목 전체에 getSignal(refresh=true)가 호출되고 추천이 1회 적재된다
  • 일부 종목 ML 실패 시 나머지 종목은 적재된다 (부분 실패 격리)
  • enabled=false면 스케줄러가 UseCase를 호출하지 않는다
  • 동일 시점 재실행(수동) 시 멱등하게 처리된다 (upsert)
  • 전체 실패 시 Discord 알림이 호출된다
  • 적재 소요·실패 메트릭이 계측된다