대부분의 API 팀은 코드를 먼저 작성한 뒤 사양을 생성합니다. 이 방식에서는 구현과 계약이 쉽게 어긋납니다. Git-네이티브 API 디자인은 순서를 바꿉니다. API 계약을 코드처럼 저장소에 두고, 브랜치에서 변경하며, 풀 리퀘스트로 검토하고, 병합된 사양에서 mock, 테스트, 문서를 생성합니다.
이 글은 특정 도구 사용법보다 Git을 중심으로 API 계약을 운영하는 실무 흐름에 집중합니다. 목표는 단순합니다. Git 기록이 곧 API 변경 기록이 되도록 만드는 것입니다.
Spec-First 툴링의 개념을 이미 알고 있고 제품 기반 워크플로를 보고 싶다면 Git-네이티브 API 워크플로를 참고하세요.
API 작업에서 "Git-네이티브"란 무엇인가
Git-네이티브 API 디자인은 API 정의를 일반 텍스트 파일로 저장소에 두는 방식입니다. 예를 들어 OpenAPI 계약은 다음처럼 코드 옆에 위치합니다.
repo/
api/
openapi.yaml
src/
tests/
핵심은 main 브랜치의 사양 파일이 진실의 원천이라는 점입니다. GUI, 문서, mock 서버, 코드 생성 결과는 모두 이 파일에서 파생됩니다.
클라우드 기반 API 디자인 도구에서는 계약이 공급업체의 백엔드에 저장되는 경우가 많습니다. 이때 저장소에는 오래된 export 파일만 남을 수 있습니다. 반대로 Git-네이티브 모델에서는 변경 이력, 책임 추적, 롤백, 리뷰가 모두 Git 안에서 처리됩니다.
Git-네이티브 설정은 세 가지 조건을 갖습니다.
- API 사양이 저장소 안의
.yaml,.json,.proto, SDL 같은 텍스트 파일이다. - 변경은 브랜치, 커밋, PR, 병합으로 처리된다.
- mock, 테스트, 문서, 클라이언트 생성은 커밋된 사양에서 수행된다.
Git에서 API를 설계해야 하는 이유
API 계약도 코드와 같은 수준으로 관리해야 합니다.
1. 변경 이력을 바로 확인할 수 있다
cursor 페이지네이션 파라미터가 언제 추가되었는지 확인하려면 다음 명령으로 충분합니다.
git log -p api/openapi.yaml
커밋 메시지, 작성자, 날짜, diff가 모두 남아 있습니다.
2. 책임 추적이 가능하다
특정 필드가 왜 추가되었는지 알고 싶다면 git blame을 사용합니다.
git blame api/openapi.yaml
각 줄이 어떤 커밋과 PR에서 들어왔는지 추적할 수 있습니다.
3. 롤백이 쉽다
잘못된 API 변경이 병합되었다면 해당 커밋을 되돌립니다.
git revert <commit-sha>
사양이 되돌아가면 mock, 문서, 코드 생성 결과도 되돌린 사양을 기준으로 다시 만들 수 있습니다.
4. 구현 전에 디자인을 검토할 수 있다
풀 리퀘스트는 API 디자인을 검토하기에 가장 적절한 장소입니다. 필수 필드 추가, enum 변경, 경로명 변경 같은 결정은 구현 전에 논의해야 비용이 작습니다.
계약이 main 브랜치의 단일 진실 원천이 되면 프론트엔드, 백엔드, QA, 문서 팀이 모두 같은 YAML을 기준으로 작업합니다. 이것이 Git 기반 API 사양 워크플로의 핵심입니다.
Git-네이티브 API 설계 루프
기본 루프는 다음 순서입니다.
- 브랜치 생성
- API 계약 작성
- 커밋
- PR 생성
- 리뷰 및 병합
- 병합된 사양 기준으로 구현
예를 들어 사용자 송장 목록 API를 추가한다고 가정합니다.
git checkout -b feat/api-invoices-list
api/openapi.yaml에 경로를 추가합니다.
paths:
/users/{userId}/invoices:
get:
operationId: listUserInvoices
summary: List invoices for a user
parameters:
- name: userId
in: path
required: true
schema:
type: string
format: uuid
- name: status
in: query
required: false
schema:
type: string
enum: [draft, open, paid, void]
responses:
"200":
description: A page of invoices
content:
application/json:
schema:
$ref: "#/components/schemas/InvoiceList"
"404":
description: User not found
작고 명확한 커밋을 만듭니다.
git add api/openapi.yaml
git commit -m "Add GET /users/{userId}/invoices contract"
PR에서는 다음을 검토합니다.
- 경로명이 기존 규칙과 일치하는가
-
statusenum 값이 충분한가 -
404가 적절한 응답인가 - 페이지네이션이 필요한가
- 응답 스키마가 기존 모델과 일관적인가
PR이 승인되면 병합합니다. 구현은 병합된 사양을 기준으로 진행합니다. 이 흐름이 사양 우선 API 개발입니다.
API 계약을 위한 브랜칭 전략
계약 변경도 코드 변경처럼 작게 나누세요. 하나의 PR에는 하나의 엔드포인트 또는 하나의 설계 변경만 포함하는 것이 좋습니다.
| 변경 유형 | 브랜치 접두사 | 예시 | 검토 중요도 |
|---|---|---|---|
| 새 엔드포인트 | feat/api- |
feat/api-invoices-list |
표준 |
| 추가 필드 | feat/api- |
feat/api-invoice-currency |
낮음 |
| 호환성 파괴 변경 | break/api- |
break/api-remove-legacy-id |
높음, 승인 필요 |
| 사양 버그 수정 | fix/api- |
fix/api-status-enum-typo |
낮음 |
| 리팩토링 | chore/api- |
chore/api-reorder-schemas |
낮음 |
브랜치 접두사는 변경의 의미를 드러냅니다. break/api-는 소비자 영향 검토가 필요하다는 신호입니다. chore/api-는 의미론적 변경이 없다는 신호입니다.
브랜칭 모델은 대부분의 API 팀에 트렁크 기반 개발이 적합합니다.
| 모델 | 적합한 경우 | API 트레이드오프 |
|---|---|---|
| 트렁크 기반 | 지속적 배포, 소규모 팀 | 작은 계약 변경을 자주 병합하고 충돌을 줄임 |
| Gitflow | 예정된 릴리스, 규제된 배포 |
develop, release 브랜치에서 사양이 갈라질 수 있음 |
가능하면 짧게 유지되는 브랜치를 사용하세요. 장기 실행 브랜치는 YAML 병합 충돌과 사양 드리프트를 만들기 쉽습니다.
풀 리퀘스트에서 API 디자인 검토하기
사양 PR은 단순 구문 검사가 아니라 디자인 리뷰입니다. 리뷰어는 다음 질문을 기준으로 확인합니다.
기존 소비자를 깨뜨리는가
다음 변경은 호환성 파괴일 수 있습니다.
- 필드 제거
- 경로명 변경
- 응답 타입 변경
- 필수 파라미터 추가
- enum 값 제거
- 기존 nullable 필드를 non-null로 변경
반대로 enum 값 추가는 일반적으로 더 안전합니다.
parameters:
- name: status
in: query
schema:
type: string
- enum: [draft, open, paid, void]
+ enum: [draft, open, paid, void, uncollectible]
위 diff는 uncollectible 값을 추가합니다. 하지만 void를 제거했다면 기존 클라이언트가 깨질 수 있습니다.
명명 규칙이 일관적인가
예를 들어 기존 API가 복수형 리소스를 사용한다면 새 경로도 같은 규칙을 따라야 합니다.
좋음: /users/{userId}/invoices
나쁨: /users/{userId}/invoice
오류 응답도 일관되어야 합니다.
{
"code": "USER_NOT_FOUND",
"message": "User not found"
}
기존 API가 code와 message를 사용한다면 새 엔드포인트도 같은 구조를 유지하세요.
diff가 읽기 쉬운가
사양 파일은 diff 친화적으로 관리해야 합니다.
- 키 순서를 일정하게 유지
- 불필요한 재정렬 금지
- 한 PR에서 여러 경로를 동시에 수정하지 않기
- 리소스별 파일 분리 고려
- 포맷터를 CI에서 고정
리뷰어가 실제 변경을 빠르게 찾을 수 있어야 합니다.
설계에서 개발로 연결하기
계약이 main에 병합되면 모든 다운스트림 작업의 입력이 됩니다.
1. 코드 생성
OpenAPI 파일에서 서버 스텁이나 타입이 지정된 클라이언트를 생성합니다.
npx @openapitools/openapi-generator-cli generate \
-i api/openapi.yaml \
-g typescript-fetch \
-o generated/api-client
핸들러는 비즈니스 로직만 채우고, 요청/응답 형태는 계약과 맞춥니다.
2. Mock 서버
mock 서버는 병합된 사양을 읽고 예시 응답을 제공합니다. 프론트엔드는 백엔드 구현을 기다리지 않고 개발을 시작할 수 있습니다.
npx prism mock api/openapi.yaml
3. 계약 테스트
계약 테스트는 실행 중인 서버가 사양과 일치하는지 검증합니다.
예를 들어 테스트 파이프라인에서 다음을 확인합니다.
- 응답 상태 코드가 사양에 정의되어 있는가
- 응답 JSON이 스키마와 일치하는가
- 필수 필드가 누락되지 않았는가
- 정의되지 않은 타입이 반환되지 않는가
불일치가 있으면 빌드를 실패시켜야 합니다.
4. 문서 생성
API 문서는 사양에서 렌더링합니다. 계약이 바뀌면 문서도 같은 커밋에서 갱신됩니다. 별도의 수동 문서 업데이트를 줄일 수 있습니다.
원칙은 하나입니다. 모든 산출물은 커밋된 계약 파일에서 생성하세요.
확장 가능한 팀 컨벤션
팀 규모가 커질수록 컨벤션이 중요해집니다.
1. 사양 파일 구조 정하기
작은 API라면 단일 파일로 충분합니다.
api/openapi.yaml
엔드포인트가 많아지면 리소스별로 분리합니다.
api/
openapi.yaml
paths/
users.yaml
invoices.yaml
schemas/
user.yaml
invoice.yaml
빌드 단계에서 하나의 OpenAPI 파일로 번들링할 수 있습니다.
2. 버전 규칙 정하기
의미 있는 변경이 있으면 info.version을 갱신합니다.
info:
title: Billing API
version: 1.4.0
일반적인 기준은 다음과 같습니다.
- 필드 추가, 엔드포인트 추가: 마이너 버전
- 버그 수정, 설명 수정: 패치 버전
- 호환성 파괴 변경: 메이저 버전
호환성 파괴 변경은 보통 /v2/ 같은 새 경로 접두사와 함께 도입합니다.
3. 변경 로그 유지
Git 기록은 정확하지만 소비자가 읽기에는 길 수 있습니다. 사양 옆에 CHANGELOG.md를 두세요.
# Changelog
## 1.4.0
- Added `GET /users/{userId}/invoices`
- Added `uncollectible` invoice status
4. CODEOWNERS로 계약 보호
API 담당자가 계약 변경을 승인하도록 설정합니다.
# .github/CODEOWNERS
/api/openapi.yaml @api-stewards
/api/paths/ @api-stewards
/api/schemas/ @api-stewards
5. CI에서 린트 실행
사람은 포맷이 아니라 디자인을 검토해야 합니다. 스타일과 규칙 검사는 CI에 맡깁니다.
# .github/workflows/api-lint.yml
name: API 린트
on:
pull_request:
paths:
- "api/**"
jobs:
spectral:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Spectral 실행
run: npx @stoplight/spectral-cli lint api/openapi.yaml --fail-severity warn
CODEOWNERS와 CI 린팅을 함께 사용하면 모든 계약 변경에 자동 검사와 담당자 리뷰가 적용됩니다.
일반적인 함정과 피하는 방법
사양과 코드가 어긋남
가장 위험한 문제는 계약은 A라고 말하지만 서버는 B를 반환하는 상황입니다.
해결 방법:
- CI에서 계약 테스트 실행
- 사양에서 서버 스텁과 클라이언트 생성
- 수동으로 작성한 API 타입 최소화
PR이 너무 큼
20개 엔드포인트를 한 번에 추가하면 리뷰가 형식적으로 끝날 가능성이 큽니다.
해결 방법:
- PR당 하나의 엔드포인트 또는 하나의 변경만 포함
- 큰 리소스 추가는 여러 PR로 분할
- 리뷰 가능한 diff 크기 유지
생성해야 할 산출물을 수동으로 작성함
클라이언트, 문서, mock 데이터를 수동으로 작성하면 시간이 지나며 사양과 달라집니다.
해결 방법:
- 커밋된 사양에서 생성
- 생성 명령을 스크립트로 고정
- CI에서 생성 결과 검증
{
"scripts": {
"api:lint": "spectral lint api/openapi.yaml",
"api:mock": "prism mock api/openapi.yaml",
"api:generate": "openapi-generator-cli generate -i api/openapi.yaml -g typescript-fetch -o generated/api-client"
}
}
YAML 병합 충돌
장기 실행 브랜치가 같은 파일을 자주 수정하면 충돌이 늘어납니다.
해결 방법:
- 브랜치를 짧게 유지
- 트렁크 기반 개발 사용
- 리소스별 파일 분리
- 키 순서 고정
- 불필요한 재정렬 금지
Apidog의 역할
Git-네이티브 워크플로는 텍스트 편집기와 CLI만으로도 운영할 수 있습니다. 하지만 많은 팀은 Git을 진실의 원천으로 유지하면서 시각적 API 디자인 도구도 원합니다.
Apidog의 Spec-First 모드는 OpenAPI 파일을 Git 저장소에 유지하면서 양방향 동기화를 제공합니다. 계약은 저장소의 파일로 남고, Apidog의 비주얼 디자이너나 에디터는 그 파일을 다루는 인터페이스가 됩니다.
즉, 브랜치, PR, Git 기록은 그대로 유지하면서 GUI 기반 설계 경험을 사용할 수 있습니다. 설정 방법은 Spec-First 모드 문서를 참고하세요.
중요한 점은 도구 자체가 아니라 운영 원칙입니다. 저장소가 단일 진실 원천이어야 하고, 모든 변경은 Git 워크플로를 통과해야 합니다.
자주 묻는 질문
Git-네이티브 API 디자인은 OpenAPI에만 적용되나요?
아니요. 텍스트 기반 계약이라면 같은 원칙을 적용할 수 있습니다. OpenAPI, AsyncAPI, gRPC .proto, GraphQL SDL 모두 Git에서 diff, 브랜치, PR 리뷰가 가능합니다.
호환성 파괴 변경은 어떻게 처리해야 하나요?
호환성 파괴 변경은 명확하게 표시해야 합니다.
-
break/api-브랜치 접두사 사용 - 메이저 버전 증가
-
CODEOWNERS승인 필수화 - 가능하면 기존 형태와 새 형태를 함께 제공
- 폐기 일정과 마이그레이션 경로 문서화
API 사양은 코드와 같은 저장소에 있어야 하나요?
한 팀이 사양과 구현을 함께 소유한다면 같은 저장소에 두는 것이 보통 더 좋습니다. 계약 변경, 구현 변경, 계약 테스트를 하나의 PR과 하나의 CI 파이프라인에서 처리할 수 있기 때문입니다.
여러 팀이 공유 API를 독립적으로 소비하고 별도 버저닝이 필요하다면 사양 전용 저장소를 고려할 수 있습니다.
사양과 코드가 멀어지는 것을 어떻게 방지하나요?
CI에 계약 테스트를 추가하세요. 실행 중인 서버에 실제 요청을 보내고, 커밋된 사양에 대해 응답을 검증합니다. 불일치가 있으면 빌드를 실패시킵니다.
또한 사양에서 클라이언트와 서버 스텁을 생성하면 타입 수준에서도 드리프트를 줄일 수 있습니다.
결론
Git-네이티브 API 디자인은 제품이 아니라 운영 방식입니다. API 계약을 코드처럼 저장소에 두고, 브랜치에서 변경하고, PR에서 검토하고, 병합된 파일에서 mock, 테스트, 문서, 클라이언트를 생성합니다.
작게 시작하세요.
- API 사양을 저장소로 옮깁니다.
- 계약 변경 PR 리뷰를 의무화합니다.
- CI에 린트를 추가합니다.
-
CODEOWNERS로 담당자 승인을 요구합니다. - mock, 코드 생성, 계약 테스트를 순서대로 연결합니다.
이 흐름이 자리 잡으면 Git 기록은 API가 어떻게 성장했는지 보여주는 완전한 변경 기록이 됩니다. 시각적 디자인 도구가 필요하다면 Apidog의 Spec-First 모드를 사용해 Git 기반 워크플로에 어떻게 맞는지 확인해 보세요.


Top comments (0)