[STK8-03] Aggregator: 포트폴리오 분석 API (내 종목 탭)

작업 내용 (설계 의도)

Aggregator에 ManualHoldingApiControllerGetPortfolioAnalysisUseCase를 추가한다. Backend(손익)와 ML(예측)을 병렬로 호출하여 조합한다.

  • GET /api/v1/portfolio — 포트폴리오 목록 + 각 종목 손익
  • POST /api/v1/portfolio — 포트폴리오 추가 (BE 위임)
  • PUT /api/v1/portfolio/{symbol} — 수정 (BE 위임)
  • DELETE /api/v1/portfolio/{symbol} — 삭제 (BE 위임)
  • GET /api/v1/portfolio/{symbol}/analysis — 손익 + 단기/중기/장기 예측 + 매도 추천

/analysis 엔드포인트는 BE 손익 조회와 ML 예측을 코루틴 async/await로 병렬 호출한다.

다이어그램

처리 흐름

sequenceDiagram
    participant FE as Frontend
    participant AGG as Aggregator
    participant BE as Backend
    participant ML as ML
    FE->>AGG: GET /api/v1/portfolio/{symbol}/analysis
    par 병렬 호출
        AGG->>BE: GET /manual-holdings/{symbol}/pnl
        BE-->>AGG: ManualHoldingWithPnlResponse
    and
        AGG->>ML: GET /portfolio-forecast/{symbol}
        ML-->>AGG: PortfolioForecastResult
    end
    AGG-->>FE: PortfolioAnalysisResponse

클래스 의존

flowchart LR
    ManualHoldingApiController --> BeManualHoldingUseCases
    ManualHoldingApiController --> GetPortfolioAnalysisUseCase
    GetPortfolioAnalysisUseCase --> BeDomainService
    GetPortfolioAnalysisUseCase --> MlDomainService
    BeDomainService --> BeGateway
    MlDomainService --> MlGateway

테스트 케이스

  • 포트폴리오 추가/수정/삭제가 BE에 정상 위임된다
  • /analysis 호출 시 손익과 단기/중기/장기 예측이 모두 포함된 응답이 반환된다
  • BE 손익에 이익 금액과 수익률(%)이 포함된다
  • 각 horizon(short/mid/long)마다 sell_recommendation이 포함된다
  • sell_recommendation에 평균 매수가 기준 예상 손익이 포함된다
  • symbol이 포트폴리오에 없으면 404가 반환된다
  • ML 응답이 지연되어도 BE 손익은 정상 반환된다 (timeout 설정 검증)