[BE-03] 기상청 단기예보 계약 검증 + 실패 경로 graceful 정합

작업 내용 (설계 의도)

근거 TDD: ../TDD.md “실패 경로·동시성·멱등”, “Testing Plan”, ADR-002·ADR-003.

변경 사항

weather gateway는 실패 경로가 나머지 둘과 불일치(catch 없음 → 예외 전파)한다. 계약 검증 테스트와 실패 경로 정합을 한 티켓으로 묶는다(같은 impl·같은 테스트 파일을 단일 작업자가 소유해 충돌 방지).

  • KmaWeatherGatewayImpl.kt(prod)에 facility/geocoding과 동일한 try/catch graceful degradation 추가 — RestClientException/역직렬화 실패/resultCode≠00/빈 items 시 Forecast 반환 + logger.warn. happy-path·타임아웃(3s/5s) 불변.
  • fixture test/resources/fixtures/external/data-go-kr/vilage-fcst.jsonresponse.body.items.item[](category=TMP/SKY/PTY/POP/REH/WSD·fcstDate·fcstTime·fcstValue) 캡처.
  • 계약 테스트(infrastructure/weather/gateway/KmaWeatherContractTest.kt) — MockWebServer fixture → WeatherGateway.shortForecast(lat,lng)ForecastSlot 그룹핑/매핑 검증 + 실패 시 빈 Forecast degrade 검증.
  • live 태그 스펙 — DATA_GO_KR_SERVICE_KEY 존재 시 실 API 호출 무손실 역직렬화 확인, 부재 시 스킵.

롤백: impl 변경은 커밋 revert(런타임 하위 호환, happy-path 불변).

의존

  • BE-01 (계약 하네스)

다이어그램

처리 흐름

sequenceDiagram
    participant T as ContractTest
    participant M as MockWebServer
    participant G as KmaWeatherGatewayImpl
    T->>M: enqueue(vilage-fcst.json)
    T->>G: shortForecast(lat, lng)
    G->>M: GET /getVilageFcst
    M-->>G: fixture (또는 5xx)
    alt 정상
        G-->>T: Forecast(slots) 매핑 검증
    else 실패
        G-->>T: 빈 Forecast (degrade)
    end

클래스 의존

flowchart LR
    CT["KmaWeatherContractTest"] --> ECS["ExternalContractSupport(BE-01)"]
    CT --> G["KmaWeatherGatewayImpl"]
    G --> WG["WeatherGateway"]
    G --> RF["ExternalRestClientFactory"]

테스트 케이스

  • fixture의 category별 값이 동일 fcstDate·fcstTime으로 그룹핑돼 ForecastSlot(temperature=TMP, sky=SKY 등)으로 매핑된다(해피).
  • 슬롯이 date·time 오름차순으로 정렬돼 반환된다(경계).
  • 외부 5xx/timeout(RestClientException) 시 예외를 전파하지 않고 빈 Forecast를 반환한다(실패/예외 — 정합 핵심).
  • resultCode≠00 또는 빈 item[] 응답 시 빈 Forecast를 반환한다(엣지).
  • live 스펙: DATA_GO_KR_SERVICE_KEY 부재 시 스킵된다(스킵 경로).