0002. API 계약은 스펙 퍼스트 — 손으로 쓰는 OpenAPI가 원본, 코드는 CI로 검증한다

맥락 (Context)

FE·BE가 “같은 맥락을 공유하는 자리”가 필요하다는 요구에서 출발했다. 지금까지 API 계약은 ① BE springdoc이 코드에서 생성하는 스펙과 ② FE 레포에 손으로 쓴 backend-*-spec.md 6개로 이원화돼 있었고, 후자는 구현과 어긋나기 시작했다(예: backend-public-card-document-spec.md는 스스로 “과거 제안”으로 강등, live-studio.md는 FE·BE 양쪽에 중복). 핸드오프 문서 안에서도 2.2절(“스펙은 코드에서 생성”)과 2.7절(“스펙 파일을 PR로 편집”)이 충돌한 채 남아 있었다.

고려한 옵션 (Considered Options)

  1. 옵션 A: code-first 유지 — springdoc 생성 스펙이 유일한 계약. 현행.
  2. 옵션 B: 생성 스펙 커밋 + 공동 리뷰 — code-first 유지하되 CI가 openapi.yaml을 커밋, FE가 CODEOWNERS로 diff 리뷰.
  3. 옵션 C: 스펙 퍼스트 — 손으로 쓰는 openapi.yaml이 원본. 구현 전에 스펙 PR로 FE·BE 합의.

옵션별 비교

기준A code-firstB 생성 커밋C 스펙 퍼스트
FE·BE 사전 정렬없음(구현 후 인지)구현 후 리뷰구현 전 합의
규율 비용없음낮음높음(스펙 PR이 선행)
계약 변경의 가시성낮음중간높음
도구 작업없음CI 1스텝CI 검증 파이프라인

결정 (Decision)

옵션 C를 선택한다. FE 재작업의 근본 원인이 “구현 후에야 계약을 알게 되는 것”이므로, 리뷰 시점을 구현 앞으로 당기는 비용을 감수한다.

구체 규칙:

  1. 원본: tapple-be 레포의 openapi/openapi.yaml. 손으로 편집한다.
  2. 시드: 최초 1회 springdoc 생성 결과를 커밋해 시작한다. 기존 API 소급 작성 없음 — 이후 변경분부터 스펙 퍼스트.
  3. 워크플로우: API 추가·변경 = ① openapi.yaml 수정 PR → ② FE·BE 리뷰(해당 경로 CODEOWNERS에 FE 지정) → ③ 머지 후 구현 PR.
  4. 검증: springdoc은 폐기하지 않고 검증기로 강등 — CI가 코드에서 스펙을 생성해 손 스펙과 diff(openapi-diff 등), 불일치 시 실패.
  5. 맥락 분리 유지: 스펙은 “무엇”, 허브 docs/api/는 “왜·엣지케이스”(핸드오프 2.3). 허브 문서의 spec: frontmatter가 이 파일을 가리킨다.

결과 (Consequences)

  • 좋아지는 것: 계약 변경이 구현 전에 보인다. FE 레포의 손 스펙 md 관행이 사라진다(계약은 openapi.yaml, 맥락은 허브 docs/api/로 이관).
  • 감수하는 것: 작은 API 변경도 PR 2개(스펙 → 구현). 스펙 문법·검증 파이프라인 유지 비용.
  • 후속 작업: springdoc 스펙 시드 커밋, CI 검증 스텝, .github/CODEOWNERSopenapi/ 경로 FE 지정, FE 레포 backend-*-spec.md 정리(허브·스펙으로 이관 후 링크 대체).

재검토 조건

  • 스펙 PR 선행이 핫픽스를 실제로 막는 사례가 반복되면 긴급 경로(구현 PR에 스펙 변경 동봉 허용) 규칙을 추가한다.
  • 팀이 규율을 못 지켜 스펙-코드 diff 실패가 상습화되면 옵션 B로 강등을 검토한다.