본문으로 건너뛰기

web의 API 호출은 orval 생성물만 쓴다

  • 상태: 채택됨
  • 작성일: 2026-07-23
  • 관련 문서: APP-66, APP-110, APP-147, APP-204, APP-244

맥락​

서버 구현이 끝나기 전에 화면을 만들어야 했다. 손으로 타입을 쓰면 계약이 바뀔 때마다 갈라진다. 목을 손으로 쓰면 계약에 없는 응답을 그리게 된다. 갈라진 것을 알려 주는 장치도 없어서 조용히 어긋난다.

결정​

  • openapi3.yml(서버 openapi3 산출물의 미러, /internal/** 제거본)을 입력으로 pnpm orval이 TanStack Query 훅과 MSW 핸들러를 생성한다.
  • 제품 컴포넌트가 API 경로를 직접 fetch()하면 위반이다. 예외는 생성 훅이 원리적으로 닿지 못하는 세 자리뿐이다: transport 자신(lib/api/, SSE는 text/event-stream을 못 읽는다), 토큰 갱신과 순환하는 인증 경로(lib/auth/, proxy.ts), 목 전용 표면(components/mocks/).
  • openapi3.yml과 lib/api/generated/**는 손으로 고치지 않는다(hook이 막는다).
  • MSW 응답은 명시적 override로 준다. orval 기본 목 응답을 그대로 쓰면 무작위 success: false가 나와 인증이 깨진다. 시드를 고정한 faker는 정당하다.

대안​

대안 1 — 화면부터 만들고 나중에 계약 맞추기. 서버를 기다리지 않아도 되고 화면이 먼저 나온다. 기각한 이유: 계약 우선 결정(APP-66)과 정면으로 충돌한다.

대안 2 — 손으로 쓴 타입·목. 생성 파이프라인 없이 바로 시작할 수 있다. 기각한 이유: 갱신 누락이 조용히 남는다. 계약과 코드가 갈라지는 것은 파이프라인이 막아야 했다. 사람 눈에 맡길 일이 아니었다.

결과​

  • 계약이 바뀌면 타입과 목이 함께 따라온다. 어긋나면 생성 단계에서 드러난다.
  • 생성물 지도(docs/generated-api-map.md) 한 곳에서 훅 이름·import 경로·MSW 핸들러를 확인한다. 그 관례가 자리 잡았다.
  • 생성 훅이 못 닿는 세 자리는 손으로 유지해야 한다. 그만큼 파이프라인 밖 코드가 남는다.
  • orval 기본 목 응답을 그대로 못 쓰고 MSW 응답을 명시적 override로 일일이 줘야 한다.
  • 계약이 바뀌면 orval → 핸들러 → 화면 순으로 재생성·반영하는 절차 비용이 매번 붙는다. web은 서버 산출물의 미러를 따라갈 수밖에 없다.
  • 생성물이 계약의 표현력에 갇힌다. MSW가 nullable 계약의 한쪽 값만 주던 문제(APP-204)는 반대 값 시드 + 회귀 게이트로 따로 잡아야 했다.