Use Cases (업데이트: 2026. 6. 3.)

Claude Code로 OpenAPI 3.1 만들기: Swagger 검증과 TypeScript 생성

Claude Code로 OpenAPI 3.1 명세를 작성하고 Swagger 검증과 TypeScript 클라이언트 생성을 연결합니다.

Claude Code로 OpenAPI 3.1 만들기: Swagger 검증과 TypeScript 생성

Claude Code에게 OpenAPI 파일을 만들라고 하면 결과는 빠르게 나옵니다. 하지만 검증 없이 계약으로 삼으면 위험합니다. UI 문구만 보고 필드를 추정하거나, 데이터베이스에 없는 enum 값을 추가하거나, 엔드포인트마다 오류 응답 형태가 달라질 수 있습니다.

이 글에서는 Claude Code를 스키마의 최종 작성자가 아니라 API 계약을 유지하는 보조자로 사용합니다. OpenAPI 3.1 명세를 만들고, 검증하고, TypeScript 클라이언트와 서버 stub을 생성하며, AI가 만들어낸 그럴듯한 schema를 잡는 간단한 규칙도 추가합니다. OpenAPI는 REST 엔드포인트, 요청, 응답, 인증, 오류를 도구가 읽을 수 있게 표현하는 규격입니다. Swagger는 이런 문서를 편집하고 미리 보고 검토하는 도구 생태계로 자주 쓰입니다.

Masa는 작은 주문 API에서 이 흐름을 시험했습니다. 처음에는 “저장소를 읽고 OpenAPI를 만들어 줘”라고만 했고, Claude Code는 실제 도메인에 없는 cancelled 상태를 추가했습니다. 더 나은 결과는 읽을 파일, 추측 금지, 검증 명령, 미확정 항목 처리 방식을 먼저 고정했을 때 나왔습니다.

공식 문서를 기준으로 확인하세요. 2026년 6월 3일 확인 기준 OpenAPI Specification latest는 3.2.0이지만, 이 글의 예시는 생성 도구 호환성을 위해 openapi: 3.1.0으로 고정합니다. 3.1 세부 사항은 OpenAPI Specification 3.1.2, 편집과 미리보기는 Swagger Editor documentation, 생성은 OpenAPI Generator usage, typescript-fetch generator, typescript-nestjs-server generator를 기준으로 삼으세요. OpenAPI 3.1을 볼 때는 현재 Swagger 문서에서 Swagger Editor Next 지원 범위도 확인하는 편이 안전합니다.

함께 보면 좋은 글은 API 개발 가이드, API 테스트 가이드, 보안 모범 사례, CLAUDE.md 모범 사례입니다.

flowchart LR
  A["구현, DB, 테스트"] --> B["Claude Code OpenAPI 초안"]
  B --> C["Swagger와 CLI 검증"]
  C --> D["TypeScript client 생성"]
  C --> E["계약 테스트"]
  C --> F["보안 리뷰"]
  D --> G["프런트엔드/파트너 연동"]
  E --> H["CI 공개 게이트"]
  F --> H

실제 활용 사례

상황Claude Code가 할 일사람이 결정할 일
기존 API 문서화라우트, 모델, 상태 코드, 인증 경계 정리공개 필드, 호환성, 이름 규칙
계약 우선 개발OpenAPI 3.1 초안, 예시, 생성 명령비즈니스 규칙, 오류 정책, 릴리스 범위
프런트엔드 연동TypeScript client, 호출 예시, 타입 차이UX, 재시도, 사용자 메시지
계약 테스트operationId, 4xx 응답, enum, 인증 선언 점검breaking change 정책과 예외 승인
보안 리뷰공개 schema, 인증 방식, PII 후보, 내부 ID 점검법무 리스크, 감사 증거, 파트너 약속
파트너 API 공개Swagger 설명, 샘플, 인증 안내계약, rate limit, 지원 범위

코드, DB, 테스트, 제품 결정으로 확인되지 않은 내용은 공개 schema에 넣지 않는 것이 원칙입니다.

복사해서 실행하는 설정

Node.js 20 이상을 권장합니다. OpenAPI Generator는 코드 생성에 Java를 사용합니다.

mkdir openapi-claude-demo
cd openapi-claude-demo
npm init -y
npm install -D @openapitools/openapi-generator-cli js-yaml
mkdir specs generated scripts

package.json:

{
  "type": "module",
  "scripts": {
    "validate:openapi": "openapi-generator-cli validate -i specs/openapi.yaml",
    "lint:contract": "node scripts/check-openapi-rules.mjs",
    "test:contract": "node --test scripts/contract.test.mjs",
    "generate:client": "openapi-generator-cli generate -i specs/openapi.yaml -g typescript-fetch -o generated/client --additional-properties=supportsES6=true,npmName=@example/orders-client,npmVersion=0.1.0",
    "generate:server": "openapi-generator-cli generate -i specs/openapi.yaml -g typescript-nestjs-server -o generated/server",
    "contract": "npm run validate:openapi && npm run lint:contract && npm run test:contract && npm run generate:client"
  },
  "devDependencies": {
    "@openapitools/openapi-generator-cli": "latest",
    "js-yaml": "latest"
  }
}

specs/openapi.yaml:

openapi: 3.1.0
info:
  title: Orders API
  version: 0.1.0
  description: Contract-first sample API for order intake.
servers:
  - url: https://api.example.com/v1
paths:
  /orders:
    post:
      operationId: createOrder
      summary: Create an order
      tags: [orders]
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateOrderRequest"
      responses:
        "201":
          description: Order created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Order"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
  responses:
    BadRequest:
      description: Invalid request
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
    Unauthorized:
      description: Missing or invalid token
  schemas:
    CreateOrderRequest:
      type: object
      additionalProperties: false
      required: [customerEmail, items]
      properties:
        customerEmail:
          type: string
          format: email
        note:
          type: [string, "null"]
        items:
          type: array
          minItems: 1
          items:
            $ref: "#/components/schemas/OrderItemInput"
    OrderItemInput:
      type: object
      additionalProperties: false
      required: [sku, quantity]
      properties:
        sku:
          type: string
        quantity:
          type: integer
          minimum: 1
    Order:
      type: object
      additionalProperties: false
      required: [id, customerEmail, status, items, createdAt]
      properties:
        id:
          type: string
          format: uuid
        customerEmail:
          type: string
          format: email
        status:
          type: string
          enum: [pending, paid]
        items:
          type: array
          items:
            $ref: "#/components/schemas/OrderItemInput"
        createdAt:
          type: string
          format: date-time
    ErrorResponse:
      type: object
      additionalProperties: false
      required: [code, message]
      properties:
        code:
          type: string
          enum: [invalid_request, unauthorized]
        message:
          type: string

로컬 규칙 검사:

import { readFileSync } from "node:fs";

const spec = readFileSync("specs/openapi.yaml", "utf8");
const forbidden = [
  { pattern: /\bTBD\b|\bTODO\b|placeholder/i, reason: "unfinished placeholder" },
  { pattern: /nullable:\s*true/, reason: "OpenAPI 3.1 should use JSON Schema null types" },
  { pattern: /password|secret|clientSecret|apiKey/i, reason: "review sensitive fields before publishing" }
];

const errors = forbidden.filter((rule) => rule.pattern.test(spec)).map((rule) => `- ${rule.reason}`);
if (!spec.includes("additionalProperties: false")) errors.push("- schemas should explicitly decide additionalProperties");
if (errors.length > 0) {
  console.error("Contract review failed:\n" + errors.join("\n"));
  process.exit(1);
}
console.log("Contract review passed.");

CI에서 실행할 계약 테스트도 추가합니다. 이 테스트는 실제 서버를 전부 때리는 대신 공개 계약 자체를 점검합니다. operationId 안정성, 변경 작업의 인증 선언, 4xx 공통 응답, 현재 구현이 지원하는 Order.status enum을 확인합니다.

// scripts/contract.test.mjs
import { readFileSync } from "node:fs";
import assert from "node:assert/strict";
import test from "node:test";
import { load } from "js-yaml";

const doc = load(readFileSync("specs/openapi.yaml", "utf8"));
const httpMethods = new Set(["get", "put", "post", "delete", "patch", "options", "head", "trace"]);

function operations() {
  return Object.entries(doc.paths ?? {}).flatMap(([path, pathItem]) =>
    Object.entries(pathItem)
      .filter(([method]) => httpMethods.has(method))
      .map(([method, operation]) => ({ path, method, operation }))
  );
}

test("operationId is unique and camelCase", () => {
  const ids = operations().map(({ operation }) => operation.operationId);
  assert.equal(new Set(ids).size, ids.length);
  for (const id of ids) {
    assert.match(id, /^[a-z][A-Za-z0-9]*$/);
  }
});

test("mutating operations require security", () => {
  for (const { path, method, operation } of operations()) {
    if (["get", "head", "options"].includes(method)) continue;
    assert.ok(operation.security?.length, `${method.toUpperCase()} ${path} must declare security`);
  }
});

test("client-visible 4xx responses reuse shared components", () => {
  for (const { path, method, operation } of operations()) {
    for (const [status, response] of Object.entries(operation.responses ?? {})) {
      if (!status.startsWith("4")) continue;
      assert.ok(response.$ref?.startsWith("#/components/responses/"), `${method.toUpperCase()} ${path} ${status}`);
    }
  }
});

test("Order status enum matches the current implementation decision", () => {
  const status = doc.components.schemas.Order.properties.status;
  assert.deepEqual(status.enum, ["pending", "paid"]);
});

실행:

npm run validate:openapi
npm run lint:contract
npm run test:contract
npm run generate:client
npm run generate:server

Claude Code 프롬프트

주문 API의 OpenAPI 3.1 계약을 작성해 주세요.
먼저 src/routes/orders.ts, src/domain/order.ts, prisma/schema.prisma, tests/orders.test.ts를 읽으세요.
코드, schema, 테스트, 제품 메모로 확인되지 않은 필드, enum, 오류 코드는 만들지 마세요.
불확실한 내용은 x-claude-review에 적고 공개 schemas에는 넣지 마세요.
nullable: true는 쓰지 마세요. operationId는 안정적인 camelCase로 작성하세요.
보호된 작업에는 security를 선언하세요. 내부 필드, PII, secret 후보는 공개 schema 전에 x-claude-review로 분리하세요.
npm run validate:openapi, npm run lint:contract, npm run test:contract를 실행하고 실패를 수정하세요.
diff, TypeScript client 호출 예시, 보안 리뷰 메모, 사람이 확인할 질문을 반환하세요.

계약 테스트와 보안 리뷰

SaaS나 파트너 API에서는 OpenAPI 리뷰를 문서 교정으로 끝내면 부족합니다. spec 리뷰, 생성 client 리뷰, 계약 테스트, 보안 리뷰를 분리하세요. spec 리뷰는 path, method, schema, status, example을 봅니다. 생성 client 리뷰는 typescript-fetch 타입과 optional/null 처리를 확인합니다. 계약 테스트는 인증 누락, 오류 응답 drift, enum 변경을 막습니다. 보안 리뷰는 공개 필드, PII, 내부 ID, server URL, secret 후보를 확인합니다.

Claude Code는 diff 정리, 테스트 초안, 리뷰 표 작성을 맡길 수 있습니다. 그러나 어떤 필드가 공개 가능한지, rate limit와 SLA를 어떻게 약속할지, 감사 증거를 얼마나 보관할지는 사람이 결정해야 합니다. API integration lead라면 PR 완료 조건에 “생성 client로 한 번 호출”, “계약 테스트 통과”, “security와 공개 schema 확인”을 넣는 것이 좋습니다.

흔한 실패

가장 흔한 문제는 구현되지 않은 cancelled, refunded, adminNote 같은 schema를 추가하는 것입니다. 그다음은 오류 응답 형태가 일관되지 않은 경우입니다. OpenAPI 3.1 문서에 nullable: true를 쓰는 것도 피해야 합니다. 생성된 client나 server stub을 손으로 고치는 것도 위험합니다. Swagger 화면만 보고 검토를 끝내면 인증 누락, operationId 중복, enum 호환성 문제가 남습니다.

교육 및 컨설팅 CTA

OpenAPI 정리는 교육과 컨설팅에 잘 맞습니다. 결과물이 명확하기 때문입니다: 명세 파일, TypeScript client, 서버 stub, 계약 테스트, 검증 명령, CI 체크리스트. 개인은 ClaudeCodeLab 제품 템플릿에서 prompt와 리뷰표를 시작할 수 있습니다. 팀이 실제 repository 기준으로 CLAUDE.md, API 테스트, 보안 리뷰까지 정리하려면 Claude Code 교육 및 상담을 사용할 수 있습니다.

직접 검증한 결과

Masa의 테스트에서 자유 프롬프트는 미구현 상태를 추가하고 오류 구조를 바꿨습니다. 필수 파일, 추측 금지, openapi-generator-cli validate, 로컬 규칙 검사, node --test 계약 테스트를 넣자 사람의 리뷰는 공개 필드, enum 확장 가능성, 인증 오류 문구, 내부 정보 노출 여부에 집중되었습니다. OpenAPI는 AI에게 맡겨 버리는 문서가 아니라 팀과 AI가 함께 보는 계약으로 사용할 때 가장 안정적이었습니다.

#Claude Code #OpenAPI #Swagger #API design #REST
무료

무료 PDF: Claude Code 치트시트

이메일을 입력하면 명령, 리뷰 습관, 안전한 워크플로를 정리한 PDF를 받을 수 있습니다.

개인정보를 안전하게 관리하며 스팸을 보내지 않습니다.

Masa

작성자 소개

Masa

Claude Code 실무 워크플로와 팀 도입을 검증하는 엔지니어입니다.