0002. API 계약은 스펙 퍼스트 — 손으로 쓰는 OpenAPI가 원본, 코드는 CI로 검증한다
- 상태: Accepted
- 결정일: 2026-07-24
- 결정자: (기입)
- 관련 문서: api-docs-multirepo-handoff.md
맥락 (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)
- 옵션 A: code-first 유지 — springdoc 생성 스펙이 유일한 계약. 현행.
- 옵션 B: 생성 스펙 커밋 + 공동 리뷰 — code-first 유지하되 CI가
openapi.yaml을 커밋, FE가 CODEOWNERS로 diff 리뷰. - 옵션 C: 스펙 퍼스트 — 손으로 쓰는
openapi.yaml이 원본. 구현 전에 스펙 PR로 FE·BE 합의.
옵션별 비교
| 기준 | A code-first | B 생성 커밋 | C 스펙 퍼스트 |
|---|---|---|---|
| FE·BE 사전 정렬 | 없음(구현 후 인지) | 구현 후 리뷰 | 구현 전 합의 |
| 규율 비용 | 없음 | 낮음 | 높음(스펙 PR이 선행) |
| 계약 변경의 가시성 | 낮음 | 중간 | 높음 |
| 도구 작업 | 없음 | CI 1스텝 | CI 검증 파이프라인 |
결정 (Decision)
옵션 C를 선택한다. FE 재작업의 근본 원인이 “구현 후에야 계약을 알게 되는 것”이므로, 리뷰 시점을 구현 앞으로 당기는 비용을 감수한다.
구체 규칙:
- 원본:
tapple-be레포의openapi/openapi.yaml. 손으로 편집한다. - 시드: 최초 1회 springdoc 생성 결과를 커밋해 시작한다. 기존 API 소급 작성 없음 — 이후 변경분부터 스펙 퍼스트.
- 워크플로우: API 추가·변경 = ①
openapi.yaml수정 PR → ② FE·BE 리뷰(해당 경로 CODEOWNERS에 FE 지정) → ③ 머지 후 구현 PR. - 검증: springdoc은 폐기하지 않고 검증기로 강등 — CI가 코드에서 스펙을 생성해 손 스펙과 diff(openapi-diff 등), 불일치 시 실패.
- 맥락 분리 유지: 스펙은 “무엇”, 허브
docs/api/는 “왜·엣지케이스”(핸드오프 2.3). 허브 문서의spec:frontmatter가 이 파일을 가리킨다.
결과 (Consequences)
- 좋아지는 것: 계약 변경이 구현 전에 보인다. FE 레포의 손 스펙 md 관행이 사라진다(계약은 openapi.yaml, 맥락은 허브
docs/api/로 이관). - 감수하는 것: 작은 API 변경도 PR 2개(스펙 → 구현). 스펙 문법·검증 파이프라인 유지 비용.
- 후속 작업: springdoc 스펙 시드 커밋, CI 검증 스텝,
.github/CODEOWNERS에openapi/경로 FE 지정, FE 레포backend-*-spec.md정리(허브·스펙으로 이관 후 링크 대체).
재검토 조건
- 스펙 PR 선행이 핫픽스를 실제로 막는 사례가 반복되면 긴급 경로(구현 PR에 스펙 변경 동봉 허용) 규칙을 추가한다.
- 팀이 규율을 못 지켜 스펙-코드 diff 실패가 상습화되면 옵션 B로 강등을 검토한다.