프론트 개발자가 화났습니다. "어제까지 응답에 있던 userName 필드가 오늘 사라졌어요. 화면이 다 깨졌습니다." 백엔드는 "아, name으로 바꿨는데요"라고 답합니다. 공지도, 버전도 없었습니다.
또 다른 회의에선 "그 API 응답이 어떻게 생겼죠?"를 두고 FE·BE가 서로 다른 그림을 그리고 있습니다.
API는 서비스들이 대화하는 '계약'입니다. 이 계약이 명확하고 안정적이면 팀이 병렬로 빠르게 달리고, 흔들리면 서로의 작업이 끊임없이 깨집니다.
- 1REST의 자원(경로)·행위(HTTP 메서드) 모델을 설명할 수 있다
- 2API 계약(요청/응답 스키마)이 FE↔BE 병렬 개발을 가능케 함을 설명할 수 있다
- 3REST와 GraphQL의 차이를 "고정 응답 vs 필드 선택"으로 구분할 수 있다
- 4API breaking change를 버전·deprecated·공지로 안전하게 내는 법을 설명할 수 있다
REST — 자원과 행위
경로는 '무엇을', 메서드는 '어떻게'
REST API는 자원(경로) 과 행위(HTTP 메서드) 의 조합으로 표현합니다.
GET /orders 주문 목록 조회 (안전·멱등: 여러 번 호출해도 같음)
POST /orders 새 주문 생성 (호출마다 새로 생김)
GET /orders/42 42번 주문 조회
PATCH /orders/42 42번 주문 일부 수정
DELETE /orders/42 42번 주문 삭제 (멱등: 여러 번 = 결과 같음)
요청/응답은 보통 JSON:
요청 Body: {"productId": 7, "qty": 2}
응답 Body: {"orderId": 42, "status": "created"}
핵심 규약: GET은 데이터를 바꾸지 않는다(안전), 같은 요청을 여러 번 해도 결과가 같아야 하는 것이 멱등성([[requirements-prd]]의 중복 처리와 연결). HTTP 상태코드(200/201/400/404/500…)로 결과를 알립니다 — 깊은 코드 해석은 [[glossary-observability]]·networking 트랙에서.
계약 — 먼저 합의하면 병렬로 달린다
API 계약: FE와 BE가 동시에 일하게 하는 약속
[[dev-roles-collaboration]]에서 본 'FE↔BE 인터페이스'가 바로 API 계약입니다. 계약은 다음을 정합니다.
계약(예: OpenAPI 명세):
엔드포인트: POST /orders
요청 스키마: { productId: number(필수), qty: number(필수, ≥1) }
응답 스키마: { orderId: number, status: "created"|"failed" }
에러 형식: { code: string, message: string } (예: 400, 409)
계약을 먼저 합의하면:
- FE는 그 계약으로 목(mock) 서버를 띄워 화면을 병렬 개발
- BE는 그 계약에 맞춰 구현
- 둘이 만나는 지점에서 어긋남이 최소화
도구: OpenAPI(Swagger) 명세로 계약을 코드처럼 관리하고, 스키마 검증(JSON Schema) 으로 요청/응답이 계약을 지키는지 자동 확인합니다. 계약이 문서로 고정되면 "그 API 어떻게 생겼죠?"라는 소모적 질문이 사라집니다.
계약 준수·변경 점검 — 직접 확인
연동 문제나 화면 깨짐이 의심되면, 실제 응답을 계약과 대조합니다. 코드를 못 짜도 할 수 있는 점검입니다.
# 실제 응답 구조 확인
curl -s https://api.example.com/orders/42 | jq .
# 계약(OpenAPI)에 명시된 필드와 실제 응답 필드 대조
curl -s https://api.example.com/orders/42 | jq 'keys'
# 기대: ["orderId","status","items","createdAt"]
$ curl -s .../orders/42 | jq 'keys'
["orderId", "name", "items"] ← 계약엔 "userName"인데 실제는 "name"!
+ "status","createdAt" 누락
→ 계약과 응답 불일치. FE 화면 깨짐의 원인. 누가 언제 바꿨나 추적([[git-fundamentals]])
curl -s https://api.example.com/orders/42 | jq .- jq keys로 응답 필드 목록을 계약과 대조 — 계약에 있는데 응답에 없으면(필드 삭제/이름변경) breaking change. FE 깨짐의 직접 원인
- HTTP 상태코드를 먼저 본다: 2xx면 성공인데 형식이 다른 것, 4xx면 요청 잘못, 5xx면 서버 장애 — 방향이 완전히 다르다([[glossary-observability]])
- 같은 요청을 두 번 보냈을 때 POST가 주문을 2개 만들면 멱등성 미보장 → 중복 결제 위험. Idempotency-Key 적용 여부 확인
- 응답 필드가 바뀌었으면 git에서 "언제 누가" 바꿨는지 추적해, 버전/공지 없이 낸 breaking change인지 확인([[semantic-versioning]])
상황: 백엔드가 정리 차원에서 응답 필드 userName을 name으로 바꿨는데, 공지·버전 없이 배포돼 프론트엔드와 외부 연동사의 코드가 일제히 깨집니다.
원인: 계약을 깨는 변경(breaking change)을 통보·버전 없이 냈습니다. API 응답 형식은 클라이언트와의 계약인데, 한쪽이 임의로 바꾸면 그 형식에 의존하던 모든 곳이 무너집니다([[semantic-versioning]]).
진단:
# 계약(OpenAPI) diff 또는 응답 필드 변화 확인
git log -p -- openapi.yaml | grep -A2 -B2 "userName\|name"
해결: (1) 즉시 호환 복구 — 옛 필드(userName)를 유지하면서 새 필드(name)를 함께 제공(둘 다 응답). (2) 변경은 버전 분리(/v2) 또는 deprecated 표기 + 유예 기간 + 사전 공지로. (3) CI에 계약 호환성 검사(OpenAPI diff)를 넣어 breaking change가 게이트에서 잡히게([[cicd-pipeline]]). API는 '내 코드'가 아니라 '여러 팀·외부와의 계약'이라는 인식이 핵심입니다.
인프라/플랫폼 관점에서 API 계약은 [[api-gateway-webhook]]·게이트웨이 설정, 버전 라우팅(/v1, /v2), 스키마 검증·레이트리밋의 기준이 됩니다. CI에 OpenAPI 호환성 검사를 넣어 breaking change를 머지 게이트에서 막는 것도 플랫폼팀의 역할입니다. PM은 API 계약을 'FE·BE·외부 연동사가 공유하는 합의서'로 관리해, 변경 시 영향 범위(누가 이 API를 쓰나)와 마이그레이션 일정을 조율합니다. 특히 외부에 공개된 API의 breaking change는 연동사 신뢰와 직결되므로, deprecated 정책과 공지 채널을 미리 갖춰야 합니다.
다음 모듈에서는 이런 서비스들이 환경에 흔들리지 않고 동작하도록 만드는 설계 원칙 — 12-Factor App을 다룹니다.