API 계약이란 무엇인가? 정의, 예제 & 모범 사례

모든 통합 실패에는 같은 근본 원인 이야기가 있습니다. A팀이 엔드포인트를 만들었습니다. B팀이 그것을 소비했습니다. "필드 이름은 userId다"와 "사실 이제 user_id다" 사이 어딘가에서 무언가가 프로덕션에서 깨졌고, 두 팀은 누구의 API 이해가 옳은지를 두고 워룸에서 오후를 보냈습니다.

해결책은 더 나은 커뮤니케이션이 아닙니다. 그것은 더 나은 산출물입니다: API 계약. API가 무엇을 하는지에 대한, 양측이 빌드하고, 검증하고, 서로에게 책임을 물을 수 있는 하나의 형식적이고 합의된 정의입니다.

이 가이드는 API 계약이 무엇인지, 서로 다른 API 스타일에 사용되는 형식, 계약 우선 대 코드 우선 개발, API 계약 테스트가 어떻게 작동하는지, 그리고 시간이 지나도 계약을 신뢰할 수 있게 유지하는 모범 사례를 다룹니다.

API 계약이란 무엇인가?

API 계약은 API 인터페이스에 대해 형식적으로 합의된 명세입니다. 그것은 다음을 정밀하고 명확하게 정의합니다:

  • 연산 — API가 노출하는 엔드포인트, 메서드, 쿼리, 또는 프로시저. REST API의 경우 경로와 HTTP 동사입니다. gRPC의 경우 서비스와 RPC입니다. 이벤트 기반 API의 경우 채널과 메시지 타입입니다.
  • 요청 및 응답 스키마 — 교환되는 데이터의 정확한 형태: 필드 이름, 타입, 필수 대 선택, 형식, 제약.
  • 오류 의미론 — 실패가 어떤 모습인지. 어떤 오류 코드가 존재하고, 무엇을 의미하며, 오류 응답이 어떤 구조를 따르는지.
  • 인증 및 인가 — 호출자가 자신을 어떻게 식별하는지: API 키, OAuth 스코프, JWT 클레임, mTLS.
  • 버전 관리 및 안정성 규칙 — 인터페이스의 어떤 부분이 안정적인지, 변경이 어떻게 도입되는지, 폐기가 어떻게 작동하는지, 그리고 제공자가 어떤 보장(속도 제한, SLA)을 약속하는지.

핵심 단어는 합의된입니다. 계약은 단지 코드가 오늘 우연히 하는 일에 대한 설명이 아닙니다. 그것은 제공자와 그 소비자 사이의 약속입니다: "이것이 인터페이스이고, 우리는 경고 없이 그것을 깨지 않겠다." 그 약속이 독립적인 개발을 가능하게 합니다. 프론트엔드 팀은 백엔드가 아직 작성되는 동안에도 계약을 상대로 빌드할 수 있습니다. 파트너는 당신의 소스 코드를 읽지 않고도 통합할 수 있습니다.

OpenAPI 파일로부터 클라이언트 SDK를 생성하거나, 명세로부터 서비스를 모킹하거나, 게시된 스키마를 깬다는 이유로 풀 리퀘스트를 거절해 본 적이 있다면, 당신은 API 계약을 그것이 의도된 대로 — 인터페이스의 진실의 원천으로 — 사용한 것입니다.

API 계약 형식: API 스타일마다 하나씩

보편적인 계약 형식은 없습니다. 보편적인 API 스타일이 없기 때문입니다. 각 프로토콜 가족은 자체 명세 표준으로 수렴했습니다.

REST / HTTP API를 위한 OpenAPI

OpenAPI(이전의 Swagger)는 HTTP API를 위한 지배적인 계약 형식입니다. OpenAPI 문서는 경로, 연산, 매개변수, 요청 본문, 응답 스키마, 인증 방식, 서버를 — 모두 YAML 또는 JSON으로 — 설명합니다.

paths:
  /orders/{orderId}:
    get:
      summary: Get an order by ID
      parameters:
        - name: orderId
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: The order
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Order"
        "404":
          description: Order not found

OpenAPI를 둘러싼 생태계가 그것의 진짜 강점입니다: 인터랙티브 문서 뷰어, 클라이언트 및 서버 코드 생성기, 목 서버, 검증기, 린터 모두가 같은 파일을 소비합니다.

gRPC를 위한 Protocol Buffers

gRPC API는 Protocol Buffers를 사용하여 .proto 파일에 정의됩니다. proto 파일이 계약입니다 — 그것은 서비스, RPC 메서드, 강타입 메시지를 정의하며, 클라이언트와 서버 코드 모두 그것으로부터 생성됩니다.

service OrderService {
  rpc GetOrder(GetOrderRequest) returns (Order);
}

message GetOrderRequest {
  string order_id = 1;
}

gRPC에서 코드 생성이 필수이기 때문에, 명세와 구현 사이의 계약 표류는 REST에서보다 구조적으로 더 어렵습니다. 번호가 매겨진 필드는 또한 명시적인 진화 정책을 인코딩합니다: 필드를 추가할 수는 있지만, 번호를 다시 매기거나 용도를 변경하면 호환성이 깨집니다.

GraphQL API를 위한 GraphQL SDL

GraphQL은 계약이 프로토콜 자체에 내장되어 있습니다. 스키마 정의 언어(Schema Definition Language, SDL)는 API가 지원하는 모든 타입, 쿼리, 뮤테이션, 구독을 설명하며, 서버가 그것을 강제합니다: 스키마와 일치하지 않는 요청은 어떤 리졸버가 실행되기 전에 거부됩니다. 인트로스펙션은 소비자가 언제나 라이브 API로부터 현재 계약을 가져올 수 있음을 뜻합니다.

이벤트 기반 API를 위한 AsyncAPI

비동기 API — Kafka 토픽, RabbitMQ 큐, NATS 서브젝트, WebSocket — 는 수년간 문서화의 무법지대였습니다. AsyncAPI는 OpenAPI의 접근법을 이벤트 기반 시스템에 적응시켜 그것을 바꿨습니다. AsyncAPI 문서는 채널, 그 위의 연산(전송/수신), 메시지 페이로드, 브로커 바인딩을 설명합니다. "누가 무엇을 발행하고, 누가 그것을 소비하는가?"가 일상적 질문인 아키텍처에서, AsyncAPI 계약은 답과 고고학 프로젝트의 차이입니다.

AI 에이전트를 위한 MCP 도구 스키마

가장 새로운 계약 타입은 서비스 간 인터페이스를 전혀 설명하지 않습니다. Model Context Protocol(MCP)은 서비스가 AI 에이전트에게 도구를 노출할 수 있게 하며, 각 도구에는 이름, 설명, 그리고 입력을 위한 JSON Schema가 따라옵니다. 그 도구 목록은 진정한 API 계약입니다 — 어쩌면 더 큰 이해관계가 걸린 것입니다. 왜냐하면 그것은 자율 에이전트가 당신의 시스템에 무엇을 할 수 있도록 허용되는지를 정의하기 때문입니다. 우리는 MCP 도구를 API 계약으로 취급하는 것과 그것들이 왜 REST 엔드포인트와 같은 문서화 엄격함을 받을 자격이 있는지에 대해 심도 있게 다뤘습니다.

요점: 당신의 API 스타일이 무엇이든, 그것을 위한 기계 판독 가능한 계약 형식이 존재합니다. 현대 시스템은 보통 여러 개를 한 번에 필요로 합니다 — 공개 API에는 REST, 내부에는 gRPC, 이벤트에는 AsyncAPI, 에이전트에는 MCP — 그것이 바로 계약이 다섯 개의 흩어진 리포지토리보다 하나의 거처에서 혜택을 보는 이유입니다.

계약 우선 vs 코드 우선 개발

계약이 존재하게 되는 방식에는 두 가지가 있으며, 그 선택이 전체 API 워크플로우를 형성합니다.

계약 우선(설계 우선)

계약 우선 개발에서는 어떤 구현을 작성하기 전에 명세를 작성합니다. OpenAPI 파일이나 proto 정의가 설계되고, 검토되고, 합의됩니다 — 그다음 제공자와 소비자 양쪽이 그것을 상대로, 종종 병렬로 빌드합니다.

장점:

  • 병렬 개발. 소비자는 제공자가 구현하는 동안 클라이언트를 생성하고 목을 상대로 빌드할 수 있습니다. 아무도 기다리지 않습니다.
  • 코드 리뷰 전 설계 리뷰. YAML diff에서 필드 이름을 두고 논쟁하는 것이 배포된 엔드포인트를 리팩터링하는 것보다 훨씬 저렴합니다.
  • 일관성. 계약을 의도적인 산출물로 설계하면 API 전반에 걸쳐 명명 규칙, 페이지네이션 패턴, 오류 형식을 강제하는 것이 자연스러워집니다.
  • 소비자 초점. 기존 데이터 모델에 가장 쉽게 붙일 수 있는 인터페이스가 아니라, 소비자가 필요로 하는 인터페이스를 설계합니다.

단점:

  • 더 많은 사전 프로세스. 내부 엔드포인트를 반복하는 두 명짜리 팀에게는 형식적 설계 단계가 부담일 수 있습니다.
  • 구현이 계약을 상대로 검증되지 않으면 표류의 위험 — 그것들을 정직하게 유지하려면 도구(검증 미들웨어, CI 검사)가 필요합니다.

코드 우선

코드 우선 개발에서는 구현을 작성하고 그것으로부터 계약을 생성합니다 — 어노테이션, 리플렉션, 또는 프레임워크 인트로스펙션이 OpenAPI 문서나 GraphQL 스키마를 만들어냅니다.

장점:

  • 소규모 팀을 위한 속도. 별도의 설계 단계가 없습니다. 계약은 언제나 코드로부터 도출 가능합니다.
  • 구성상 표류 없음. 생성된 명세는 구현으로부터 나오기 때문에 구현과 일치합니다.

단점:

  • 계약이 약속이 아니라 부산물이 됩니다. 코드가 하는 것이 무엇이든 그것이 API입니다 — 우연적인 부분까지 포함해서.
  • 호환성을 깨는 변경이 쉽게 빠져나갑니다. 인터페이스를 인터페이스로서 검토하도록 강제하는 것이 없기 때문입니다.
  • 생성된 명세는 종종 평범합니다: 설명 누락, 모호한 오류 문서, 예제 없음.

어느 쪽을 써야 할까?

실용적인 경험 법칙: API에 소비자가 많을수록, 그리고 그들을 덜 통제할수록, 계약 우선이 더 큰 값을 합니다. 공개 API, 파트너 통합, 별도 팀 간의 계약은 계약 우선 처리를 받을 자격이 있습니다. 같은 팀이 소유한 하나의 프론트엔드가 소비하는 내부 엔드포인트는 코드 우선일 수 있습니다 — 생성된 계약이 여전히 게시되고, 버전 관리되고, 호환성을 깨는 변경에 대해 검사되는 한.

많은 성숙한 팀은 하이브리드에 안착합니다: 속도를 위한 코드 우선에, 계약 우선의 안전성 대부분을 주는 계약 수준 CI 게이트(호환성을 깨는 변경 탐지, 스키마 린팅)를 더한 것.

API 계약 테스트

아무것도 검증하지 않는 계약은 소망일 뿐입니다. API 계약 테스트는 제공자와 소비자가 실제로 합의된 인터페이스를 준수하는지 자동으로 확인하는 실천입니다. 세 가지 기법이 지배적입니다.

소비자 주도 계약 테스트

Pact가 대중화한 소비자 주도 계약 테스트에서, 각 소비자는 자신이 의존하는 특정 상호작용을 기록합니다: "내가 /orders/123을 GET하면, id, status, total을 포함하는 본문과 함께 200을 기대한다." 이 기록된 기대는 계약을 형성하며, 그것은 제공자의 CI 파이프라인에서 제공자를 상대로 재생됩니다.

이 접근법의 힘은 정밀성입니다. 제공자는 각 소비자가 실제로 어떤 필드를 사용하는지 정확히 알게 됩니다. 필드를 제거하고 싶으신가요? 계약 테스트는 어떤 소비자가 깨질지를 — 배포 후가 아니라 배포 전에 — 즉시 알려줍니다.

CI에서의 스키마 검증

더 단순하고 더 광범위한 기법: 구현이 게시된 명세와 일치하는지 검증하는 것.

  • 서비스를 상대로 요청을 실행하고 응답을 OpenAPI 스키마에 대조하여 검증합니다.
  • 계약을 준수하지 않는 응답을 거부하는 검증 미들웨어를 사용합니다(스테이징에서 훌륭함).
  • 완전성과 스타일을 위해 명세 자체를 린트합니다(Spectral 및 유사 도구).

이것은 가장 흔한 실패 모드 — 명세는 한 가지를 말하고 코드는 다른 것을 한다 — 를 저렴하고 지속적으로 잡아냅니다.

호환성을 깨는 변경 탐지

마지막으로, 계약 자체를 diff합니다. oasdiff(OpenAPI), Buf(protobuf), GraphQL Inspector 같은 도구는 명세의 새 버전을 이전 버전과 비교하고 각 변경을 분류합니다: 추가적(안전), 또는 호환성을 깸(제거된 필드, 변경된 타입, 새로운 필수 매개변수). 이것을 CI에 연결하면 호환성을 깨는 변경이 — 소비자에게 조용한 깜짝 선물이 아니라 — 명시적이고 의도적인 승인을 요구하는 실패한 빌드가 됩니다.

이 섹션에서 단 한 가지만 한다면, 이것을 하세요. 호환성을 깨는 변경 탐지는 설정이 저렴하고 가장 아픈 실패를 잡아냅니다.

API 계약이 아키텍처 문서에 속하는 이유

대부분의 팀이 놓치는 부분이 여기 있습니다. 아름다운 OpenAPI 파일, 엄격한 Pact 스위트, CI의 호환성을 깨는 변경 게이트를 가질 수 있습니다 — 그럼에도 무언가가 바뀌어야 할 때 중요한 질문에 답하지 못할 수 있습니다: "누가 이 계약에 의존하는가?"

리포지토리의 계약 파일은 인터페이스를 설명하지만, 그 컨텍스트에 대해서는 아무것도 말하지 않습니다. 어떤 서비스가 그것을 구현하는가? 어떤 서비스, 프론트엔드, 파트너가 그것을 소비하는가? 이 엔드포인트를 폐기하면, 실제로 무엇이 깨지는가? 그 지식은 보통 사람들의 머릿속에 살며, 이는 누군가 팀을 옮길 때마다 그것이 저하된다는 뜻입니다.

이것이 바로 아키텍처 문서와 API 계약이 서로를 필요로 하는 지점입니다:

  • 아키텍처 컨텍스트 없는 계약은 보이지 않게 노후화됩니다. 작년에 재작성된 서비스를 설명하는 고아가 된 openapi.yaml을 아무도 알아채지 못합니다. 왜냐하면 그것이 설명하는 시스템에 그것을 연결하는 것이 아무것도 없기 때문입니다.
  • 계약 없는 아키텍처 다이어그램은 부정확합니다. 두 상자 사이의 "REST/JSON"이라고 레이블이 붙은 화살표는 관계가 존재한다는 것을 알려주지만, 그것을 가로질러 무엇이 흐르는지는 알려주지 않습니다. 계약이 화살표에 의미를 부여하는 것입니다.

C4 모델은 이 연결을 위한 자연스러운 구조를 제공합니다: 계약은 그것을 구현하고 소비하는 컨테이너와 컴포넌트에 붙습니다(그 용어에 대한 빠른 복습은 C4 모델 용어집 항목을 참고하세요). API Gateway 컨테이너는 그 OpenAPI 계약을 지닙니다. 내부 마이크로서비스는 그 proto 파일을 지닙니다. Kafka 중심 서비스는 그 채널을 정의하는 AsyncAPI 문서를 지닙니다.

이것이 정확히 Archyl의 API Contracts 기능이 작동하는 방식입니다: OpenAPI, gRPC, GraphQL, AsyncAPI, 또는 MCP 계약을 — git에서 동기화하거나 직접 붙여넣어 — 임포트하고, 그것들을 아키텍처 모델의 C4 요소에 연결합니다. 그 연결은 양방향입니다: 계약으로부터 어떤 요소가 그것을 구현하고 소비하는지 보고, 다이어그램의 어떤 요소로부터든 그 인터페이스를 설명하는 실제 명세를 열 수 있습니다. 계약이 바뀌면, 부족 지식으로부터 의존성 그림을 재구성하는 대신, 아키텍처의 어떤 부분이 폭발 반경에 있는지 한눈에 볼 수 있습니다. 우리는 API Contracts: 아키텍처에 연결된 당신의 API 명세에서 그 기능을 자세히 다뤘습니다.

원칙은 도구와 상관없이 성립합니다: 계약은 아무도 열지 않는 폴더가 아니라, 그것이 묶는 아키텍처 요소 옆에 살 때 가장 가치가 있습니다.

API 계약 모범 사례: 체크리스트

계약은 오래 사는 약속이므로, 그렇게 다루세요:

  • 단일 진실의 원천을 확립하세요. 계약마다 하나의 표준 위치. 명세가 세 곳에 존재한다면, 그것은 영(零) 곳에 존재하는 것입니다. 그것이 git 리포지토리든 Archyl 같은 아키텍처 플랫폼이든, 권위 있는 버전이 어디에 사는지 모두가 알아야 합니다.
  • 명시적으로 버전 관리하세요. 모든 계약에 버전을 부여하고, 버전 올림이 무엇을 의미하는지 정의하세요. 시맨틱 버저닝이 잘 작동합니다: 추가적 변경은 마이너 버전을, 호환성을 깨는 변경은 메이저 버전을 올립니다.
  • 메이저 버전 없이 절대 깨지 마세요. 필드 제거, 타입 변경, 필수 매개변수 추가, 검증 강화 — 모두 호환성을 깸. 그것들은 새 메이저 버전이나 새 엔드포인트, 더하기 마이그레이션 경로를 요구합니다.
  • 폐기 정책을 작성하고 지키세요. 명세에서 폐기된 연산을 표시하고, 종료 날짜를 알리며, 소비자에게 현실적인 기간(며칠이 아니라 몇 달)을 주고, 제거 전에 사용량을 모니터링하세요.
  • 계약 변경을 코드 변경처럼 검토하세요. 스키마 diff는 적어도 구현 diff만큼의 검토를 받을 자격이 있습니다 — 그것은 더 많은 소비자를 가집니다.
  • 강제를 자동화하세요. CI에서의 스키마 검증과 호환성을 깨는 변경 탐지. 사람은 계약에 합의하고, 기계는 그것을 강제합니다.
  • 해피 패스뿐만 아니라 오류와 인증도 문서화하세요. 400과 401이 소비자가 디버깅 시간을 쓰는 곳입니다. 그것들을 명시하세요.
  • 계약을 아키텍처에 연결하세요. 모든 계약은 그것을 구현하는 컴포넌트와 소비하는 컴포넌트로 추적 가능해야 합니다. 그래서 영향 분석이 조사가 아니라 조회가 되도록.

자주 묻는 질문

API 계약과 API 문서의 차이는 무엇인가요?

API 문서는 사람을 위해 작성됩니다: 가이드, 튜토리얼, 예제, 개념 설명. API 계약은 사람과 도구 모두가 소비하는 형식적이고 기계 판독 가능한 명세입니다 — 그것은 코드를 생성하고, 요청을 검증하고, 목을 구동하고, CI 빌드를 실패시킬 수 있습니다. 좋은 문서는 종종 계약으로부터 생성되지만, 계약은 구속력 있는 산출물입니다: 문서는 API를 설명하고, 계약은 그것을 정의합니다.

계약 우선 개발이란 무엇인가요?

계약 우선(또는 설계 우선) 개발은 구현하기 전에 API 명세 — OpenAPI 문서, proto 파일, 또는 GraphQL 스키마 — 를 작성하고 합의하는 것을 의미합니다. 그다음 소비자와 제공자가 같은 합의된 인터페이스를 상대로 병렬로 빌드합니다. 그것은 설계 논의를 앞당기고, 병렬 작업을 가능하게 하며, 계약을 코드의 부산물이 아니라 의도적인 약속으로 만듭니다.

API 계약 테스트란 무엇인가요?

API 계약 테스트는 제공자와 소비자가 합의된 인터페이스를 준수하는지 자동으로 검증합니다. 여기에는 소비자 주도 계약 테스트(Pact 스타일, 소비자 기대가 제공자를 상대로 재생됨), CI에서의 스키마 검증(구현이 명세와 일치하는지 확인), 호환성을 깨는 변경 탐지(명세 버전을 diff하여 릴리스 전에 비호환 변경을 표시)가 포함됩니다.

내부 API에도 계약이 필요한가요?

네 — 어쩌면 더 필요합니다. 왜냐하면 내부 API는 더 빠르게 바뀌고 더 적은 의례로 보호되기 때문입니다. 계약은 더 가벼울 수 있지만(코드 우선 생성으로 충분), 여전히 게시되고, 버전 관리되고, 호환성을 깨는 변경에 대해 검사되어야 합니다. API 변경으로 인한 대부분의 프로덕션 사고는 내부 API 변경으로 인해 발생합니다.


API 계약에 아키텍처 안의 거처를 줄 준비가 되셨나요? Archyl의 API Contracts 기능을 탐색하세요 — OpenAPI, gRPC, GraphQL, AsyncAPI, MCP 계약을, 당신의 C4 모델에 연결하여. 또는 계속 읽어보세요: API Contracts: 아키텍처에 연결된 당신의 API 명세 | MCP 도구를 API 계약으로 | C4 모델이란 무엇인가? 완벽 가이드.