개념
API 문서와 실제 동작이 다를 때
"API 문서에는 email 필드가 필수라고 되어 있는데, 실제로는 안
보내도 요청이 성공한다" — 이런 문서와 실제 동작의 불일치는
실무에서 흔하다. 이 API를 쓰는 다른 팀(프런트엔드, 다른
마이크로서비스)은 문서를 믿고 코드를 짜기 때문에, 이 불일치가
연쇄적인 결함으로 이어진다.
OpenAPI 스펙이란
OpenAPI(예전 이름 Swagger)는 API의 엔드포인트, 요청·응답 형식, 필수/선택 필드, 데이터 타입을 표준화된 형식(보통 YAML이나 JSON)으로 기술하는 명세다.
paths:
/orders:
post:
requestBody:
content:
application/json:
schema:
type: object
required: [productId, quantity]
properties:
productId:
type: string
quantity:
type: integer
minimum: 1
responses:
'201':
content:
application/json:
schema:
type: object
properties:
orderId:
type: string
totalPrice:
type: number이 명세는 "주문 생성 API는 productId와 quantity가 필수이고,
quantity는 1 이상이어야 하며, 성공 시 orderId와 totalPrice를
포함한 응답을 준다"는 약속(Contract) 을 기계가 읽을 수 있는
형태로 적어둔 것이다.
계약 테스트(Contract Testing)란
이 명세(계약)와 실제 API 응답이 정확히 일치하는지 자동으로 검증하는 것이 계약 테스트다.
계약 테스트 = "명세에 적힌 대로 실제 API가 동작하는가?"를 자동 검증레슨 2의 어서션(totalPrice가 정확히 9000인지 확인)이 개별
값의 정확성을 확인한다면, 계약 테스트는 응답의 구조 자체가
명세와 일치하는지(필드가 다 있는가, 타입이 맞는가)를 확인한다는
점에서 다르다.
test("주문 생성 API 응답이 OpenAPI 스펙과 일치한다", async () => {
const response = await request(app).post("/orders").send(validOrder)
expect(response.status).toBe(201)
expect(response.body).toMatchSchema(orderResponseSchema) // 스펙 기반 스키마 검증
})계약 테스트가 잡아내는 결함
- 개발자가 API를 수정하면서 문서(스펙) 업데이트를 깜빡했다
- 필드 이름을 실수로 바꿨는데(
totalPrice→total_price) 아무도 눈치 못 챘다 - 필수였던 필드를 선택으로 바꿨는데, 이걸 쓰는 다른 팀에 미리 알리지 않았다
이런 결함은 모듈 6(레슨 1)의 결함 생명주기 관점에서 보면, API를 사용하는 쪽(프런트엔드, 다른 서비스)에서 늦게 발견될수록 더 큰 비용이 든다 — 계약 테스트는 이걸 API가 배포되는 시점에 즉시 잡아낸다.
스키마 검증과 값 검증은 함께 쓰인다
계약 테스트가 "구조가 맞는지"를 확인한다고 해서 레슨 2·3에서 배운 "값이 정확한지" 확인이 필요 없어지는 건 아니다. 두 검증은 서로 보완하는 관계다 — 구조가 맞아도 계산이 틀릴 수 있고, 계산이 맞아도 구조가 깨질 수 있다.
실무에서 왜 필요한가
여러 팀이 API를 공유하는 조직(마이크로서비스 환경)에서, "이 API를 쓰는 다른 팀에 미리 알리지 않고 응답 구조를 바꿔서 장애가 났다"는 사고는 매우 흔하다. 계약 테스트를 CI(모듈 14)에 포함시켜 두면, API 제공자가 계약을 어기는 변경을 하려는 순간 배포 전에 자동으로 걸러진다 — 이건 모듈 8(레슨 4)에서 배운 "테스트 완료 단계에서 배운 점을 조직 전략에 반영한다"는 원칙을, 아예 자동화된 안전장치로 만든 것이다.