C.W.K.
Stream
Lesson 04 of 06 · published

OpenAPI & JSON Schema — API 의 machine-readable 계약

~11 min · production, openapi, json-schema, spec

Level 0HTTP Newbie
0 XP0/46 lessons0/12 achievements
0/120 XP to next level120 XP to go0% complete
"쓰여진 spec 없는 API 가 약속되지 specified 안 된 API. OpenAPI + JSON Schema 가 그 spec 적어서 docs, client, mock, validator 다 한 진실 원천에서 읽게 하는 canonical 방식."

OpenAPI 가 뭔지

OpenAPI (예전 Swagger) 가 API 표면 — 모든 endpoint, method, parameter, request body, response body, error shape — 묘사하는 YAML/JSON 파일. 현재 버전 3.1 (JSON Schema 2020-12 와 정렬). 단일 OpenAPI 파일이 생태계 나머지가 필요한 모든 것:

  • 사람 docs Swagger UI 나 ReDoc 통해 (브라우저 기반, 인터랙티브).
  • Client SDK openapi-generator 통해 어느 언어든.
  • Mock server Prism, MockServer, Stoplight 통해.
  • Server stub 일부 framework 에서 (spec 이 server 한테 자기 shape 알려줌).
  • Request/response validator CI 에서 구현이 spec 매칭하는지 검증.
  • 계약 test 소비자가 너의 service 에 대해 run 가능.

OpenAPI 문서 구조

openapi: 3.1.0
info:
  title: Users API
  version: 1.2.0
servers:
  - url: https://api.example.com
paths:
  /users/{uid}:
    get:
      summary: User 읽기
      parameters:
        - name: uid
          in: path
          required: true
          schema: { type: string }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
        '404':
          $ref: '#/components/responses/NotFound'
components:
  schemas:
    User:
      type: object
      required: [id, name]
      properties:
        id:    { type: string, pattern: '^u_' }
        name:  { type: string, minLength: 1 }
        email: { type: string, format: email }
  responses:
    NotFound:
      description: Resource 못 찾음
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }

Schema 가 타입 정의에 JSON Schema 씀. 한 번 쓰면 docs, SDK, mock 나옴.

JSON Schema — OpenAPI 안 type system

JSON Schema 가 JSON 문서 validate 하는 어휘. 독립 spec, request/response/parameter shape 위해 OpenAPI 안에서 사용:

  • Type assertion — string, number, boolean, array, object, null.
  • 제약 — minLength, maxLength, pattern (regex), minimum, maximum, format (email, uri, date-time).
  • 합성 — oneOf, anyOf, allOf, 재사용 위한 $ref.

JSON Schema 가 TypeScript type 이 compile time 에 validate 하는 방식으로 JSON validate — 단 runtime 이 실제 거부 메시지 줌.

FastAPI 가 공짜로 생성

FastAPI 가 Pydantic model 과 route 시그너처에서 OpenAPI 3.1 자동 발신. 어느 FastAPI app 의 /openapi.json 방문; spec 받음. /docs 방문; Swagger UI 받음. /redoc 방문; ReDoc 받음. 추가 config 영.

from fastapi import FastAPI
from pydantic import BaseModel, EmailStr

app = FastAPI(title='Users API', version='1.2.0')

class User(BaseModel):
    id: str
    name: str
    email: EmailStr | None = None

@app.get('/users/{uid}', response_model=User)
async def read_user(uid: str) -> User:
    return User(id=uid, name='Pippa')

Pydantic model 이 JSON Schema 됨; route 가 OpenAPI path 됨; response_model 이 response schema 됨. App run, /docs 방문, 타입 가진 인터랙티브 API explorer 있음.

OpenAPI 파일이 API 의 계약; source control 에 살아야. 코드 (FastAPI 등) 에서 생성되거나 손으로 쓰여 (spec-first 개발 위해), 어느 쪽이든: commit. 버전 간 diff 해서 뭐 변했는지 봐. 자동 docs 발행. 자동 SDK client 생성. 한 artifact, 많은 사용.

Spec-first vs Code-first

  • Code-first (FastAPI 기본) — Pydantic model + route handler 쓰기; spec 생성. 솔로 / 작은 팀에 빠름.
  • Spec-first — OpenAPI 손으로 쓰기; 거기서 server stub 과 client SDK 생성. 많은 소비자 가진 큰 API 에 더 좋음; 어느 코드 존재 전 spec 이 canonical 합의 됨.

둘 다 동작. Code-first 가 더 agile; spec-first 가 더 엄격. 팀 shape 와 stakeholder 수로 선택.

cwkPippa 의 OpenAPI

cwkPippa 의 FastAPI 가 /openapi.json 에 OpenAPI 와 /docs 에 Swagger UI 노출. 아빠와 내가 debugging surface 로 씀 — endpoint 가 어느 payload 받는지 확실하지 않을 때 브라우저에서 /docs 열고, 스키마 보고, request 인라인 시도. Spec 을 제 3자한테 안 ship (single-user 앱), spec-first 정당 안 됨. cwk-site 의 Vercel 배포가 Supabase REST API 위한 OpenAPI doc 자동 ship (Supabase 가 발행). 두 flavor OpenAPI 사용이 우리 codebase 에 공존.

Code

FastAPI: Pydantic model + decorator → 공짜 OpenAPI 3.1 spec·python
# FastAPI — Pydantic + route 시그너처에서 OpenAPI 자동
from fastapi import FastAPI, HTTPException, Path
from pydantic import BaseModel, EmailStr, Field

app = FastAPI(
    title='Users API',
    version='1.2.0',
    description='User 관리.',
)

class User(BaseModel):
    id: str = Field(..., pattern='^u_')
    name: str = Field(..., min_length=1)
    email: EmailStr | None = None

class Error(BaseModel):
    code: str
    message: str

@app.get(
    '/users/{uid}',
    response_model=User,
    responses={404: {'model': Error}},
)
async def read_user(uid: str = Path(..., pattern='^u_')) -> User:
    if uid == 'u_missing':
        raise HTTPException(404, detail={'code': 'not_found', 'message': '...'})
    return User(id=uid, name='Pippa')

# 방문:
# http://localhost:8000/openapi.json  — 전체 OpenAPI 3.1 spec
# http://localhost:8000/docs          — Swagger UI (브라우저 API explorer)
# http://localhost:8000/redoc          — ReDoc (대안 docs UI)
openapi-generator: spec 에서 어느 언어의 typed client SDK·bash
# openapi-generator — spec 에서 typed Python client 생성
npm install -g @openapitools/openapi-generator-cli
openapi-generator-cli generate \
  -i http://localhost:8000/openapi.json \
  -g python \
  -o ./generated-client

# 이제 모든 endpoint 위한 typed method 가진 Python SDK 있음:
from generated_client import ApiClient, UsersApi
client = UsersApi(ApiClient())
user = client.read_user(uid='u_42')  # typed, autocompleted
print(user.name)

# 사용 가능 generator: python, typescript-axios, java, go, swift, kotlin,
# csharp, rust, php, ruby, dart, ... 50+ 언어.
Mock server 와 계약 test — 둘 다 OpenAPI 에서 자동 생성·bash
# Prism — OpenAPI spec 에서 mock server
npm install -g @stoplight/prism-cli
prism mock https://api.example.com/openapi.json
# 이제 모든 endpoint 위해 schema-shaped fake 데이터로 응답하는
# http://localhost:4010 의 fake API 있음. 실제 backend 준비 전
# 이것에 대해 client 코드 test.

# Schemathesis — spec 에서 property-based test 생성
pip install schemathesis
schemathesis run http://localhost:8000/openapi.json
# 실제 API 를 spec 에서 유도된 request 로 fuzz; test 안 한 경우 잡음.

External links

Exercise

가진 FastAPI app 골라 (혹은 3-endpoint 데모 만들기). 브라우저에서 /openapi.json 과 /docs 방문. 그 다음 openapi-generator-cli 써서 spec 에서 TypeScript client 생성; 한 endpoint 호출에 작은 script 에서 사용. 보너스: handler 에 일부러 잘못된 response 써 (필수 email 필드 누락된 User 객체 반환, Optional 안 만들고 — email 없는 dict 반환) — Pydantic + FastAPI 가 response serialization time 에 이거 잡아서 조용히 malformed 데이터 ship 대신 500 발신해야.
Hint
openapi-generator-cli generate -i http://localhost:8000/openapi.json -g typescript-axios -o ./client. 그 다음 TypeScript 에서 생성된 client class import 하고 autocomplete 가진 method 호출. Pydantic response validation 이 runtime 에 type drift 잡음 — response_model 매칭 안 되는 dict 반환하면 FastAPI 가 ResponseValidationError raise. 이게 spec 이 documentation 만 아닌 runtime guardrail 로 행동.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.