--- id: wiki-2026-0508-api-first-architecture title: API-First Architecture category: 10_Wiki/Topics status: verified canonical_id: self aliases: [API-first design, Contract-first, Schema-first] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [architecture, api, openapi, contract-first, design] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: yaml framework: OpenAPI 3.1, Stoplight, Spectral, buf --- # API-First Architecture ## 매 한 줄 > **"매 API spec 의 first artifact — code follows contract"**. 매 design-first → spec → mock → impl 매 separate workflow. 매 Swagger (2010) → OpenAPI 3.0 (2017) → 3.1 (2021, JSON Schema 2020-12 align) → AsyncAPI 2.6/3.0 (events) → buf (gRPC). 매 2026 modern stack 은 spec-driven codegen + lint (Spectral) + breaking change detection (buf, oasdiff) + AI-assisted spec (Claude Opus 4.7). ## 매 핵심 ### 매 workflow 1. **Design**: write OpenAPI/proto spec 매 먼저. 2. **Review**: stakeholders (FE/BE/partner) review spec, not code. 3. **Mock**: Prism/Stoplight serve mock from spec. 4. **Generate**: SDK (oapi-codegen, openapi-typescript), server stubs. 5. **Implement**: fill stubs, validate at runtime. 6. **Test**: contract tests against spec. 7. **Govern**: lint (Spectral), diff (oasdiff), versioning. ### 매 vs code-first - **Code-first**: write handler → annotate → generate spec. Drift risk, late stakeholder feedback. - **API-first**: write spec → generate handler → fill. Single source of truth. ### 매 응용 1. **Public SaaS API** — Stripe-style spec-driven. 2. **Multi-platform SDK distribution** — auto-generated TS/Python/Go/Java clients. 3. **Frontend/backend parallel dev** — FE works against mock from day 1. 4. **B2B integration contracts** — partners review spec before impl. ## 💻 패턴 ### OpenAPI 3.1 spec ```yaml openapi: 3.1.0 info: { title: Orders API, version: 1.0.0 } paths: /orders/{id}: get: operationId: getOrder parameters: - { name: id, in: path, required: true, schema: {type: string} } responses: "200": description: OK content: application/json: schema: { $ref: "#/components/schemas/Order" } "404": { $ref: "#/components/responses/NotFound" } components: schemas: Order: type: object required: [id, status] properties: id: { type: string, format: uuid } status: { type: string, enum: [pending, paid, shipped] } total: { type: number, minimum: 0 } ``` ### Mock server (Prism) ```bash npx @stoplight/prism-cli mock spec.yaml --port 4010 # Now FE devs hit http://localhost:4010/orders/123 with realistic responses ``` ### Spectral lint config ```yaml # .spectral.yaml extends: [[spectral:oas, all]] rules: operation-operationId-unique: error operation-tag-defined: error no-eval-in-markdown: error custom-versioned-path: given: "$.paths" severity: error then: function: pattern functionOptions: { match: "^/v\\d+/" } ``` ### Type-safe client (openapi-typescript) ```bash npx openapi-typescript spec.yaml -o src/api-types.ts ``` ```typescript import createClient from "openapi-fetch"; import type { paths } from "./api-types"; const client = createClient({ baseUrl: "https://api.example.com" }); const { data, error } = await client.GET("/orders/{id}", { params: { path: { id: "abc" } }, }); // data: Order | undefined, fully typed ``` ### Server stub (oapi-codegen, Go) ```bash oapi-codegen -package api -generate types,server spec.yaml > api/api.gen.go ``` ```go type ServerImpl struct{ db *sql.DB } func (s *ServerImpl) GetOrder(c echo.Context, id string) error { var o Order if err := s.db.QueryRow("SELECT ...").Scan(...); err != nil { ... } return c.JSON(200, o) } ``` ### Breaking change detection (oasdiff) ```bash oasdiff breaking spec-v1.yaml spec-v2.yaml --fail-on ERR # CI gate: blocks PR if breaking change without major version bump ``` ### AsyncAPI for events ```yaml asyncapi: 3.0.0 info: { title: Orders Events, version: 1.0.0 } channels: orderCreated: address: orders.created messages: OrderCreatedMessage: payload: $ref: "#/components/schemas/Order" operations: publishOrderCreated: action: send channel: { $ref: "#/channels/orderCreated" } ``` ### buf for gRPC governance ```yaml # buf.yaml version: v2 modules: - path: proto breaking: use: [FILE] lint: use: [DEFAULT] except: [PACKAGE_VERSION_SUFFIX] ``` ## 매 결정 기준 | 상황 | Spec | |---|---| | HTTP REST, public | OpenAPI 3.1 | | Async events | AsyncAPI 3.0 | | gRPC | proto + buf | | GraphQL | SDL + Apollo Federation | | Internal-only, single team | Code-first (faster iteration) | **기본값**: 매 OpenAPI 3.1 + Spectral lint + Prism mock + openapi-typescript codegen + oasdiff CI. ## 🔗 Graph - 부모: [[API Fundamentals]] - 응용: [[OpenAPI]] - Adjacent: [[Consumer-Driven Contracts]] · [[API Gateway]] ## 🤖 LLM 활용 **언제**: 매 multi-team API, 매 public SDK distribution, 매 partner integration, 매 FE/BE parallel dev. **언제 X**: 매 prototype, 매 throwaway script, 매 single-developer monolith. ## ❌ 안티패턴 - **Spec-as-documentation only**: 매 not source of truth, 매 drift. 매 codegen-driven 의 enforce. - **No CI lint**: 매 spec rot. 매 Spectral / vacuum 의 사용. - **Big-bang spec**: 매 review fatigue. 매 incremental + path-scoped reviews. - **No mock**: 매 FE blocked on BE. 매 Prism mock 의 day-1 deploy. ## 🧪 검증 / 중복 - Verified (OpenAPI 3.1 spec, AsyncAPI spec, Stoplight docs, buf docs, ThoughtWorks Tech Radar "Design APIs first"). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — full content (API-first workflow + tooling) |