본문으로 건너뛰기
yutils

JSON Schema vs TypeScript — 어느 쪽에서 어느 쪽으로 생성할까

JSON Schema 가 할 수 있는 것 vs TypeScript 가 할 수 있는 것, 양방향 생성 전략, Ajv/Zod 런타임 검증, API 경계 패턴.

약 9분 읽기

팀에서 자주 나오는 질문 — JSON Schema 를 진실의 원천으로 두고 TS 타입을 생성할까, 반대로 TS 가 원천이고 JSON Schema 가 부산물일까, 아니면 둘 다 별도로 두고 매번 동기화 할까. 이 가이드는 둘이 무엇을 할 수 있는지 (서로 다른 강점), 양방향 생성의 트레이드오프, Ajv/Zod 같은 런타임 검증 도구의 자리, API 경계에서 어떻게 박는지 정리한다.

둘은 무엇이 다른가

TypeScriptJSON Schema
실행 시점컴파일 타임 (런타임 X)런타임 (Ajv, Hyperjump 등)
표현력유니온, 제네릭, 매핑 타입, 조건부 타입 등 풍부구조 + 제약 (min/max, format, pattern)
제약 검증형태 검증만 (number 가 양수인지 X)minimum / maxLength / format: email 등 풍부
언어 호환TS / JS 만모든 언어 (Python, Java, Go, Rust 등)
도구tsc, IDE 인텔리센스Ajv (검증), 코드 생성, OpenAPI 통합

핵심 트레이드오프

TS 타입은 컴파일 타임에만 살아 있다. 런타임에 외부에서 들어온 JSON 이 정말 그 타입인지 검증할 수 없다. JSON Schema 는 정반대 — 런타임 검증이 본업이지만 IDE/컴파일러는 모름.

실무에서 풀어야 할 문제: API 경계에서 받은 데이터를 안전하게 TS 타입으로 좁히기. 두 시스템 사이에 자동 다리를 놓아야 한다.

옵션 A — JSON Schema 가 원천 (OpenAPI 환경)

백엔드 / 외부 API 가 OpenAPI 또는 JSON Schema 를 발행하는 경우. 이미 존재하는 schema 를 정답으로 두고 TS 타입을 생성.

도구:

  • openapi-typescript — OpenAPI YAML/JSON → .d.ts 파일.
  • json-schema-to-typescript — pure JSON Schema → TS interface.
  • quicktype — 다언어 타입 생성. JSON 샘플도 입력 가능.

장점: schema 변경이 즉시 TS 컴파일 에러로 드러남. 백엔드와 동기화 보장.

단점: TS 의 풍부한 타입 표현은 못 만듦. 예: type ID = string & { __brand: "UserId" } 같은 브랜드 타입은 schema 에 표현 불가.

옵션 B — TypeScript 가 원천 (TS-first 백엔드)

풀스택 TS (tRPC, Next.js Route Handlers) 또는 TS-first 백엔드. Zod/io-ts/Valibot 같은 라이브러리로 타입 + 런타임 검증을 함께 정의.

import {z} from "zod";

const User = z.object({
  id: z.string().uuid(),
  email: z.string().email(),
  age: z.number().int().min(0),
});

type User = z.infer<typeof User>; // TS 타입 자동 도출

// 런타임 검증
const parsed = User.parse(input); // throws on invalid

장점:

  • 진실의 원천 1 개. TS 타입과 런타임 검증이 분리될 수 없음.
  • TS 표현력 100% 활용. branded type, 유니온 narrowing 등.
  • schema 정의 자체가 코드 — 리팩터링·검색·테스트 모두 익숙한 방식.

단점:

  • 다른 언어로 schema 공유 어려움. Zod → JSON Schema 변환기 (zod- to-json-schema) 가 있지만 일부 표현은 손실.
  • TS 가 없는 클라이언트 (모바일·외부 통합) 에는 결국 별도 schema 필요.

옵션 C — JSON 샘플로 시작 (실무에서 가장 흔함)

외부 API 응답을 받아 빠르게 타입 만들고 시작. 진실의 원천이 schema 가 아니라 실제 응답 — 가장 빠른 진입.

도구:

  • JSON → TypeScript — JSON 입력 → TS interface 추론. 중첩 객체는 별도 interface 로 분리. 0 dependency, 브라우저에서 즉시.
  • JSON Schema 생성기 — 같은 JSON 에서 JSON Schema Draft 2020-12 자동 생성. 타입/required/중첩 추론.

흐름: 응답 JSON → TS interface 와 JSON Schema 둘 다 생성 → TS 는 코드 에 사용, schema 는 검증·문서화에 사용.

런타임 검증 — Ajv 와 Zod

Ajv (JSON Schema)

가장 빠른 JSON Schema 검증기. 정의된 schema 를 컴파일해 검증 함수를 생성 (한 번 컴파일, 반복 호출).

import Ajv from "ajv";
const ajv = new Ajv();
const validate = ajv.compile({
  type: "object",
  properties: {
    email: {type: "string", format: "email"},
    age: {type: "integer", minimum: 0}
  },
  required: ["email"]
});

if (!validate(data)) console.error(validate.errors);

Draft 2020-12 까지 지원. JSON Schema 검증기 가 Ajv 백엔드를 사용해 즉시 검증 결과를 보여준다.

Zod / Valibot (TS-first)

스키마 정의가 TS 코드. 위에서 본 패턴. 장점은 IDE 자동 완성, 단점은 다른 언어로 export 어려움.

Valibot 은 Zod 와 비슷한 API, tree-shake 가 더 우수해 번들 크기 ↓. 2026 년 기준 새 프로젝트는 Valibot 도 검토 가치.

API 경계에서 — 안전한 패턴

외부에서 들어오는 데이터는 반드시 런타임 검증 후 TS 타입으로 좁힌다.

Zod 사용 예 (Next.js Route Handler)

const Body = z.object({
  email: z.string().email(),
  password: z.string().min(8),
});

export async function POST(req: Request) {
  const json = await req.json();
  const parsed = Body.safeParse(json);
  if (!parsed.success) {
    return Response.json({error: parsed.error.format()}, {status: 422});
  }
  const {email, password} = parsed.data; // 안전한 TS 타입
  // ...
}

Ajv + 외부 schema 사용 예

import schema from "./api-response.schema.json";
import type {ApiResponse} from "./api-response";

const validate = ajv.compile<ApiResponse>(schema);

const data = await fetchSomething();
if (!validate(data)) throw new Error("Invalid response");
// data 는 이제 ApiResponse 타입

Ajv 의 제네릭 type guard 패턴. validate 가 true 반환하면 TS 가 data: ApiResponse 로 좁힘.

흔한 함정

1. TS 타입만 박고 런타임 검증 생략

fetch().then(r => r.json() as User) 는 거짓말. 런타임에 실제 모양이 다르면 silently 깨짐. 외부 데이터는 무조건 검증.

2. 두 schema 수동 동기화

TS interface 따로, JSON Schema 따로 — drift 가 시작되면 잡기 매우 어려움. 한쪽에서 생성하거나 (옵션 A/B), 같은 정의에서 둘 다 도출 (Zod + zod-to-json-schema).

3. JSON Schema 의 additionalProperties: true 디폴트

명시 안 하면 추가 필드를 허용. 무관한 필드를 탐지하려면 additionalProperties: false 명시.

4. any / unknown 으로 도망

TS 가 까다로워 any 캐스트 → 검증 의미 0. unknown + 검증 함수가 정답.

5. Branded type 을 schema 로 표현

TS 의 UserId & Brand 같은 패턴은 JSON Schema 에 직접 표현 불가. 검증 후 수동 cast 하거나, Zod 의 z.brand() 사용.

의사 결정 가이드

  • 다언어 백엔드 + OpenAPI 있음 → 옵션 A (schema 원천, TS 생성).
  • 풀 TS 스택 → 옵션 B (Zod). 다른 언어 필요 시 zod-to- json-schema.
  • 외부 API 빠르게 통합 → 옵션 C (json-to-ts 또는 quicktype 으로 시작 → 검증 추가).
  • 스토리지 (DB) 와 wire (API) 동시 정의 → Drizzle/ Prisma + Zod 또는 TypeBox 결합.

요약

  • TS = 컴파일 타임 형태. JSON Schema = 런타임 검증 + 다언어.
  • 진실의 원천 1 개로 통일. 양쪽 자동 도출 또는 한쪽에서 생성.
  • 런타임 검증 (Ajv/Zod) 없이 외부 데이터 신뢰 X.
  • 자동 도구: JSON → TypeScript · JSON Schema 생성기 · JSON Schema 검증기.
  • TS-first 새 프로젝트는 Zod/Valibot, 다언어 통합은 OpenAPI.
가이드 목록으로