--- id: wiki-2026-0508-api-contract-definition title: API Contract Definition category: 10_Wiki/Topics status: verified canonical_id: self aliases: [API Contract, OpenAPI Contract, Schema-First API, Contract-First Design] duplicate_of: none source_trust_level: A confidence_score: 0.95 verification_status: applied tags: [api, openapi, contract, schema, design] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: yaml framework: openapi-3.1 --- # API Contract Definition ## 매 한 줄 > **"매 API 의 shape (path, method, schema, error, auth) 을 매 machine-readable spec 으로 매 먼저 정의하고 매 그 후 server/client 가 매 그것을 따른다."** OpenAPI 3.1 (REST), gRPC `.proto`, GraphQL SDL, AsyncAPI (event), JSON Schema 가 매 dominant. 2026 현재 매 LLM 이 매 spec 을 읽어 매 client / mock / test 를 매 generate — 매 contract-first 가 매 AI 시대의 매 default. ## 매 핵심 ### 매 contract-first vs code-first - **Contract-first**: 매 spec → 매 server stub + 매 client SDK. 매 multi-team / multi-language 의 default. - **Code-first**: 매 code → 매 spec generated (FastAPI, NestJS Swagger). 매 single team 에서 매 빠름. - 2026 추세: 매 contract-first 가 매 LLM-friendly + 매 mock-driven dev 에 유리. ### 매 standard 별 사용처 - **OpenAPI 3.1**: 매 REST/HTTP. 매 JSON Schema 2020-12 align. - **gRPC + protobuf**: 매 internal high-perf, 매 strong typing. - **GraphQL SDL**: 매 client-driven query, 매 BFF. - **AsyncAPI 3**: 매 Kafka / NATS / WebSocket event. - **JSON Schema**: 매 payload validation, 매 LLM structured output. ### 매 contract testing - **Provider tests**: 매 server 가 매 spec 을 만족하는지 검증 (매 schemathesis, dredd, prism). - **Consumer-driven contracts**: 매 Pact — 매 client 가 매 expectation 을 매 publish, 매 server 가 매 verify. ### 매 응용 1. SDK auto-generation (Stainless, Speakeasy, OpenAPI Generator). 2. Mock server (Prism, Mockoon). 3. Fuzz / property test (Schemathesis). ## 💻 패턴 ### 1) OpenAPI 3.1 minimal ```yaml openapi: 3.1.0 info: { title: Orders API, version: 1.2.0 } servers: [{ url: https://api.example.com/v1 }] paths: /orders/{id}: get: operationId: getOrder parameters: - { name: id, in: path, required: true, schema: { type: string, format: uuid } } responses: '200': content: { application/json: { schema: { $ref: '#/components/schemas/Order' } } } '404': { description: Not found } components: schemas: Order: type: object required: [id, total] properties: id: { type: string, format: uuid } total: { type: number, minimum: 0 } status: { type: string, enum: [pending, paid, shipped, canceled] } ``` ### 2) gRPC proto ```proto syntax = "proto3"; package orders.v1; service Orders { rpc Get (GetReq) returns (Order); rpc Stream (StreamReq) returns (stream Event); } message GetReq { string id = 1; } message Order { string id = 1; double total = 2; Status status = 3; } enum Status { PENDING = 0; PAID = 1; SHIPPED = 2; CANCELED = 3; } ``` ### 3) GraphQL SDL ```graphql type Order { id: ID! total: Float! status: Status! } enum Status { PENDING PAID SHIPPED CANCELED } type Query { order(id: ID!): Order } ``` ### 4) AsyncAPI 3 (Kafka event) ```yaml asyncapi: 3.0.0 info: { title: Orders Events, version: 1.0.0 } channels: orders.created: address: orders.created messages: OrderCreated: payload: { $ref: '#/components/schemas/Order' } operations: publishOrderCreated: action: send channel: { $ref: '#/channels/orders.created' } ``` ### 5) Server stub generation ```bash # 매 OpenAPI → TypeScript Express npx openapi-generator-cli generate -i openapi.yaml -g typescript-node -o ./gen # 매 OpenAPI → Python (FastAPI server) npx openapi-typescript openapi.yaml -o gen/types.ts ``` ### 6) Schemathesis property-based test ```bash schemathesis run openapi.yaml --base-url=http://localhost:8000 \ --checks=all --hypothesis-deadline=2000 ``` ### 7) Pact consumer test (TS) ```ts import { Pact } from '@pact-foundation/pact'; const provider = new Pact({ consumer: 'web', provider: 'orders' }); await provider.addInteraction({ state: 'order 1 exists', uponReceiving: 'a get for order 1', withRequest: { method: 'GET', path: '/v1/orders/1' }, willRespondWith: { status: 200, body: { id: '1', total: 99 } } }); ``` ### 8) JSON Schema for LLM structured output ```python order_schema = { "type": "object", "required": ["id", "total"], "properties": {"id":{"type":"string"},"total":{"type":"number"}} } client.messages.create( model="claude-opus-4-7", tools=[{"name":"return_order","input_schema":order_schema}], tool_choice={"type":"tool","name":"return_order"}, messages=[...] ) ``` ## 매 결정 기준 | 상황 | Standard | |---|---| | 매 public REST | OpenAPI 3.1 | | 매 internal microservices | gRPC + protobuf | | 매 client-shaped query | GraphQL | | 매 event-driven | AsyncAPI 3 | | 매 LLM structured output | JSON Schema | **기본값**: 매 contract-first + 매 OpenAPI 3.1 + 매 SDK auto-gen + 매 schemathesis CI. 매 internal 은 gRPC. ## 🔗 Graph - 부모: [[API Design]] - 변형: [[OpenAPI]] · [[gRPC]] - 응용: [[Contract Testing]] - Adjacent: [[JSON Schema]] · [[Pact]] ## 🤖 LLM 활용 **언제**: 매 spec → SDK / mock / test scaffold 자동 생성, 매 spec lint, 매 changelog diff. **언제 X**: 매 LLM 이 매 spec 을 매 invent — 매 source-of-truth 는 매 git-tracked spec file. ## ❌ 안티패턴 - **Spec drift**: 매 server 가 매 spec 과 매 다름. 매 schemathesis CI 로 차단. - **No versioning**: 매 breaking change 를 매 same path 에 push. 매 `/v1` → `/v2` 또는 매 deprecated header. - **Anyof everywhere**: 매 spec 이 매 너무 permissive → 매 client 의 매 type narrowing 불가. - **Code-first without lint**: 매 generated spec 이 매 inconsistent (매 nullable, 매 enum). - **Mock-only test**: 매 contract test 없이 매 mock 만 사용 → 매 prod fail. ## 🧪 검증 / 중복 - Verified (OpenAPI 3.1 spec, AsyncAPI 3.0 spec, Pact docs, Schemathesis docs). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — OpenAPI/gRPC/GraphQL/AsyncAPI 4-standard + contract testing |