Files
2nd/10_Wiki/Topics/Coding/API_OpenAPI_Spec.md
T
2026-05-09 21:08:02 +09:00

6.5 KiB

id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
id title category status source_trust_level verification_status created_at updated_at tags tech_stack applied_in aliases
api-openapi-spec OpenAPI / Swagger — Schema-first vs Code-first Coding draft B conceptual 2026-05-09 2026-05-09
api
openapi
swagger
vibe-coding
language applicable_to
TS / OpenAPI
Backend
OpenAPI
Swagger
schema-first
code-first
Hono RPC
oRPC
ts-rest

OpenAPI

API contract 표준. Schema → Type generation, mock server, client SDK, docs. Schema-first 또는 Code-first. Hono / ts-rest / oRPC 가 modern code-first.

📖 핵심 개념

  • OpenAPI 3.1: 표준 spec.
  • Schema-first: yaml/json 먼저 → 코드 생성.
  • Code-first: 코드의 type → spec 자동.
  • Server / client SDK 자동.

💻 코드 패턴

Schema-first (yaml)

openapi: 3.1.0
info:
  title: Acme API
  version: 1.0.0

paths:
  /orders:
    get:
      summary: List orders
      parameters:
        - { name: limit, in: query, schema: { type: integer, default: 20 } }
        - { name: cursor, in: query, schema: { type: string } }
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderList'
    post:
      summary: Create order
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/CreateOrder' }
      responses:
        '201':
          description: created
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Order' }
        '400':
          description: validation error
          content:
            application/problem+json:
              schema: { $ref: '#/components/schemas/Problem' }

components:
  schemas:
    Order:
      type: object
      required: [id, items, total]
      properties:
        id: { type: string, format: uuid }
        items:
          type: array
          items: { $ref: '#/components/schemas/OrderItem' }
        total: { type: string }
        status: { type: string, enum: [open, paid, shipped] }
    
    Problem:
      type: object
      required: [type, title, status]
      properties:
        type: { type: string, format: uri }
        title: { type: string }
        status: { type: integer }
        detail: { type: string }

코드 생성

# Server stub
openapi-generator-cli generate -i api.yaml -g typescript-node-server -o server/

# Client SDK
openapi-generator-cli generate -i api.yaml -g typescript-axios -o client/

# 또는 modern: openapi-typescript
npx openapi-typescript api.yaml -o api-types.ts
import type { paths } from './api-types';
type CreateOrderRequest = paths['/orders']['post']['requestBody']['content']['application/json'];
type OrderResponse = paths['/orders']['post']['responses']['201']['content']['application/json'];

Code-first — Hono + zod-openapi

import { OpenAPIHono, createRoute, z } from '@hono/zod-openapi';

const app = new OpenAPIHono();

const route = createRoute({
  method: 'post',
  path: '/orders',
  request: {
    body: {
      content: { 'application/json': { schema: CreateOrderSchema } },
    },
  },
  responses: {
    201: {
      content: { 'application/json': { schema: OrderSchema } },
      description: 'Created',
    },
  },
});

app.openapi(route, async (c) => {
  const data = c.req.valid('json'); // typed
  const order = await createOrder(data);
  return c.json(order, 201);
});

// Spec 노출
app.doc('/openapi.json', { openapi: '3.1.0', info: { title: 'API', version: '1.0' } });

Code-first — ts-rest

import { initContract } from '@ts-rest/core';
const c = initContract();

export const contract = c.router({
  createOrder: {
    method: 'POST',
    path: '/orders',
    body: CreateOrderSchema,
    responses: { 201: OrderSchema, 400: ProblemSchema },
  },
});

// Server (Express / Fastify / Hono)
import { initServer } from '@ts-rest/express';
const router = initServer().router(contract, {
  createOrder: async ({ body }) => ({ status: 201, body: await create(body) }),
});

// Client (auto-typed)
import { initClient } from '@ts-rest/core';
const api = initClient(contract, { baseUrl: '...' });
const r = await api.createOrder({ body: { items: [...] } });

→ Frontend / backend 가 type 공유.

Mock server (fast prototyping)

# Prism — OpenAPI mock
prism mock api.yaml --port 4010

→ Backend 만들기 전에 frontend 시작.

Docs UI

// Swagger UI
import swaggerUi from 'swagger-ui-express';
app.use('/docs', swaggerUi.serve, swaggerUi.setup(spec));

// Scalar (modern, beautiful)
import { apiReference } from '@scalar/express-api-reference';
app.use('/docs', apiReference({ spec: { url: '/openapi.json' } }));

// Stoplight Elements

Validation (Hono / Express middleware)

// Express
import OpenApiValidator from 'express-openapi-validator';
app.use(OpenApiValidator.middleware({ apiSpec: 'api.yaml' }));
// 자동 validate body / query / response

Lint (Spectral)

npx spectral lint api.yaml
# 표준 / 일관성 검사
# .spectral.yml
rules:
  operation-tag-defined: warn
  operation-success-response: error
  no-unresolved-refs: error

Diff (breaking change)

npx oasdiff diff old.yaml new.yaml --breaking-only
# CI 에서 PR 마다

Code-first vs Schema-first

Schema-first:
+ Single source of truth
+ Multi-language (server / client)
- Sync 어려움 (yaml 과 코드)

Code-first:
+ TS type 가 진실
+ Hot reload
- 다른 언어 client = generate 필요

🤔 의사결정 기준

상황 추천
TS only fullstack ts-rest / oRPC / Hono RPC / tRPC
다양한 client 언어 Schema-first OpenAPI
Public API OpenAPI + Scalar docs
Mock first Schema-first + Prism
Strong type 일급 Code-first
빠른 prototype Code-first

안티패턴

  • Spec 과 코드 drift: schema-first 의 위험. CI 에서 검증.
  • 모든 endpoint 200 만 명시: 4xx / 5xx 같이.
  • Schema 안 example: 사용자 모름.
  • Auth 누락 (security scheme): 명시.
  • Generated client 직접 변경: 다음 generate 시 잃음.
  • OpenAPI 안 서버 검증: client 만 — server bypass 가능.
  • Versioning 없는 spec 변경: breaking.

🤖 LLM 활용 힌트

  • TS = ts-rest / Hono RPC.
  • 다중 언어 = OpenAPI yaml + generators.
  • Spectral lint + oasdiff CI.

🔗 관련 문서