[WATCH-03] BE: 뉴스 스냅샷 영속화 (DB + UseCase + API)
작업 내용 (설계 의도)
변경 사항
news_snapshots 테이블을 추가하고 watchlist 도메인에 뉴스 스냅샷 저장/조회 UseCase 및 API를 추가한다.
FE는 GET /api/v1/watchlist/{symbol}/news로 영속화된 뉴스 요약과 헤드라인을 조회한다.
스냅샷이 1시간 이상 stale이거나 없으면 ML을 호출해 갱신한다.
WATCH-01이 완료되어 ML의 /signals 응답에 headlines(list[Headline])·news_summary가 포함된 이후 진행한다.
DDL 참고
CREATE TABLE news_snapshots (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT 'PK',
symbol VARCHAR(20) NOT NULL COMMENT '종목 심볼',
summary TEXT NOT NULL COMMENT '뉴스 전체 요약',
headlines JSON NOT NULL COMMENT '헤드라인 배열 [{title,url,translated_title,sentiment}]',
refreshed_at DATETIME(6) NOT NULL COMMENT '마지막 갱신 시각',
created_at DATETIME(6) NOT NULL COMMENT '생성 시각',
UNIQUE KEY uq_symbol (symbol)
) COMMENT = '관심종목 뉴스 스냅샷 캐시';다이어그램
처리 흐름
sequenceDiagram participant FE participant BE as WatchlistApiController participant UCA as GetNewsSnapshotUseCase participant DS as NewsSnapshotDomainService participant ML as ML :8000 FE->>BE: GET /api/v1/watchlist/{symbol}/news BE->>UCA: execute(symbol) UCA->>DS: getOrRefresh(symbol) alt 캐시 신선 (1시간 이내) DS-->>UCA: NewsSnapshot else stale / 없음 DS->>ML: GET /signals/{symbol} ML-->>DS: Signal DS->>DS: save(NewsSnapshot) DS-->>UCA: NewsSnapshot end UCA-->>BE: NewsSnapshotResponse BE-->>FE: 200 OK
클래스 의존
flowchart LR WatchlistApiController --> GetNewsSnapshotUseCase WatchlistApiController --> SaveNewsSnapshotUseCase GetNewsSnapshotUseCase --> NewsSnapshotDomainService SaveNewsSnapshotUseCase --> NewsSnapshotDomainService NewsSnapshotDomainService --> NewsSnapshotRepository NewsSnapshotRepository -.->|implements| NewsSnapshotRepositoryImpl NewsSnapshotDomainService --> SignalGateway SignalGateway -.->|implements| SignalGatewayImpl
테스트 케이스
NewsSnapshotDomainService.isStale이 1시간 초과 스냅샷을 stale로 판단한다NewsSnapshotDomainService.isStale이 59분 스냅샷을 신선으로 판단한다GetNewsSnapshotUseCase가 신선한 캐시를 ML 호출 없이 반환한다GetNewsSnapshotUseCase가 stale 캐시를 ML 재호출 후 갱신해 반환한다- ML 호출 실패 시 기존 stale 스냅샷을 그대로 반환한다 (graceful degradation)
SaveNewsSnapshotUseCase가 신규 스냅샷을 upsert 저장한다NewsSnapshotRepositoryImpl이 symbol 기준 upsert를 올바르게 처리한다 (Testcontainers)GET /api/v1/watchlist/{symbol}/news가{summary, headlines, refreshedAt}를 반환한다- watchlist에 없는 symbol 조회 시 404를 반환한다