[STK9-03] Aggregator: Redis → Backend DB 교체 + refresh 로직

작업 내용 (설계 의도)

RedisSignalCacheRepositoryImplBeSignalSnapshotRepositoryImpl로 교체한다. SignalCacheRepository 인터페이스는 그대로 유지하고 구현체만 바꾼다.

SignalDomainService 로직을 반전한다. 현재는 ML을 먼저 호출하고 실패 시 캐시를 폴백으로 쓰지만, 변경 후에는 DB를 먼저 조회하고 MISS 또는 refresh일 때만 ML을 호출한다.

SignalApiController 및 UseCase에 refresh: Boolean 파라미터를 추가한다.

Redis 의존성(spring-boot-starter-data-redis, StringRedisTemplate)을 제거한다.

다이어그램

처리 흐름 — DB HIT (일반 조회)

sequenceDiagram
    participant FE as Frontend
    participant C as SignalApiController
    participant DS as SignalDomainService
    participant REPO as BeSignalSnapshotRepositoryImpl
    participant BE as Backend
    FE->>C: GET /api/v1/signals/{symbol}
    C->>DS: getSignal(symbol, refresh=false)
    DS->>REPO: findSignal(symbol)
    REPO->>BE: GET /signal-snapshots/{symbol}
    BE-->>REPO: SignalSnapshotResponse
    REPO-->>DS: SignalResult (fromCache=true)
    DS-->>C: SignalResult
    C-->>FE: SignalResponse

처리 흐름 — 새로고침

sequenceDiagram
    participant FE as Frontend
    participant C as SignalApiController
    participant DS as SignalDomainService
    participant GW as SignalGateway
    participant REPO as BeSignalSnapshotRepositoryImpl
    participant ML as ML
    participant BE as Backend
    FE->>C: GET /api/v1/signals/{symbol}?refresh=true
    C->>DS: getSignal(symbol, refresh=true)
    DS->>GW: fetchSignal(symbol)
    GW->>ML: GET /signals/{symbol}
    ML-->>GW: 신규 계산 결과
    GW-->>DS: SignalResult (fromCache=false)
    DS->>REPO: saveSignal(symbol, result)
    REPO->>BE: PUT /signal-snapshots/{symbol}
    DS-->>C: SignalResult
    C-->>FE: SignalResponse

클래스 의존

flowchart LR
    SignalApiController --> GetSignalUseCase
    GetSignalUseCase --> SignalDomainService
    SignalDomainService --> SignalCacheRepository
    SignalDomainService --> SignalGateway
    BeSignalSnapshotRepositoryImpl -.->|implements| SignalCacheRepository
    BeSignalSnapshotRepositoryImpl --> BeDomainService

테스트 케이스

  • DB에 스냅샷 있고 refresh=false → ML 호출 없이 DB 결과 반환
  • DB에 스냅샷 없고 refresh=false → ML 호출 → DB 저장 → 반환
  • refresh=true → DB 유무 관계없이 ML 호출 → DB 갱신 → 반환
  • ML 다운 + DB에 스냅샷 있음 → DB 결과 반환 (폴백)
  • ML 다운 + DB에 스냅샷 없음 → 예외 반환
  • Redis 관련 빈(StringRedisTemplate, RedisConfig 등)이 더 이상 컨텍스트에 없다
  • ?refresh=true 쿼리 파라미터가 Controller → UseCase → DomainService까지 전달된다