Skip to content
Published on

TypeScript 데이터 검증 2026 — Zod·Valibot·ArkType·TypeBox·Effect Schema 심층 비교 (AI 도구 호출 시대의 런타임 검증)

Authors

프롤로그 — 모든 API 경계가 스키마를 요구하는 시대

2022년 TypeScript 프로젝트에서 "런타임 검증 뭐 써?"라고 물으면 답은 거의 하나였다. Zod. 그 전엔 Joi, Yup, 또는 직접 짠 타입 가드.

2026년의 같은 질문엔 후보가 다섯이다. 그 사이에 일이 너무 많이 일어났다.

  • tRPC가 표준이 됐다 — 모든 절차 입력에 Zod 스키마를 박아 넣는 패턴이 풀스택 TS의 기본 문법이 됐다. 스키마 = API 경계.
  • React Hook Form + zodResolver 패턴이 폼의 사실상 표준 — 폼 검증 = 같은 스키마.
  • AI 도구 호출(function calling, tool use)이 JSON Schema를 강제 — OpenAI·Anthropic·Gemini 모두 도구 정의를 JSON Schema로 받는다. TS 스키마를 JSON Schema로 못 내보내면 곤란해졌다.
  • OpenAPI 자동 생성이 다시 핫함 — Hono·tRPC-openapi·@asteasolutions/zod-to-openapi·TypeBox의 빌트인 인터롭. 한 스키마로 클라이언트·서버·문서가 동시에 빠진다.
  • 번들 크기 = 돈 — 엣지 런타임·Cloudflare Workers에 올라가는 코드가 늘었다. Zod 1MB는 옛말이라도 KB 단위로 따지는 진영이 자랐다.

이 흐름이 다섯 진영을 만들었다.

  • Zod 4 — 사실상 표준. 4.x에서 트리 셰이킹·파서 재작성으로 약점을 정통으로 쳤다.
  • Valibot — 함수형 모듈러 API. 쓰는 만큼만 번들에 들어간다. 프런트엔드 폼·엣지에서 강함.
  • ArkType — TS 문자열을 스키마로. type({ email: 'string.email' }) 같은 식. TS 추론과 100% 호환이 무기.
  • TypeBox — 만든 스키마가 곧 JSON Schema. Fastify·OpenAPI·AI 도구 호출과 직결.
  • Effect Schema — Effect 생태계의 검증 도구. 타입드 에러·합성 가능한 변환·양방향 인코딩이 무기.
  • Yup / Joi / Superstruct — 살아 있지만 신규 채택은 점점 줄어든다.

이 글은 2026년 5월 기준 이 라이브러리들을 직접 비교한다. 번들·추론·JSON Schema·비동기·OpenAPI·런타임 비용·생태계의 7축으로. 그리고 같은 User 스키마를 5개 라이브러리로 나란히 짠다.


1장 · 풍경 — 데이터 검증 라이브러리 지도

먼저 분류부터. 모든 게 같은 자리에 있지 않다.

분류대표 도구한 줄 요약
객체 빌더 (체이닝)Zod, Yup, Joiz.string().email() 식. 가장 친숙.
모듈러 함수Valibotstring([email()]). 트리 셰이킹 친화.
TS 문법을 스키마로ArkTypeTS 타입 표현을 그대로 런타임에.
JSON Schema 네이티브TypeBox만든 게 곧 JSON Schema.
함수형 + EffectEffect Schema타입드 에러, 양방향 인코딩.
가벼운 어서션Superstruct, io-ts작고 합성 가능, 약간 옛 스타일.

이 글의 초점은 굵게 갈리는 다섯 — Zod·Valibot·ArkType·TypeBox·Effect Schema — 다. Yup·Joi·Superstruct는 7장에서 짧게 정리한다.

왜 이 다섯인가

  • Zod 4 — 신규 TypeScript 프로젝트의 기본값. tRPC·Next.js·React Hook Form 어디서나 등장. 2024년 발표된 Zod 4가 2025~2026 사이 안정화되면서 트리 셰이킹 약점이 크게 줄었다.
  • Valibot — 2024년부터 본격 성장. 함수 단위 import로 사용 안 한 코드가 번들에서 떨어진다. 프런트엔드·엣지에서 점유율을 빠르게 가져갔다.
  • ArkType — TS 표현을 그대로 받는 사고 전환이 매력. v2 안정화 이후 신규 프로젝트의 한 갈래.
  • TypeBox — Fastify 권장, AJV 결합으로 가장 빠른 런타임 중 하나. AI 도구 호출에서 JSON Schema를 직접 받아야 할 때 첫 번째 선택지.
  • Effect Schema — Effect 풀스택을 가는 팀의 검증 도구. Effect를 안 쓰면 굳이 고를 이유는 적다.

2장 · 7축으로 보는 비교 매트릭스

상세 분석으로 들어가기 전, 한눈에 보는 큰 그림.

Zod 4ValibotArkTypeTypeBoxEffect Schema
번들 (코어)중간매우 작음 (모듈러)작음매우 작음큼(Effect 포함)
TS 추론 품질매우 좋음매우 좋음최상 (TS 표현 그대로)좋음매우 좋음
JSON Schema 익스포트외부 패키지 필요외부 패키지 필요빌트인빌트인 (정의 자체가 JSON Schema)빌트인
비동기 검증OO부분X (동기 중심)O
OpenAPI 익스포트zod-to-openapi@valibot/to-json-schema빌트인빌트인 (Fastify·AJV)@effect/schema-openapi
런타임 비용빠름 (4에서 큰 폭 개선)매우 빠름빠름가장 빠름 (AJV 컴파일 시)중간~빠름
생태계가장 큼빠르게 성장중간API/Fastify 진영Effect 사용자

이 표만 보고 결정을 내려선 안 된다. 다음 장부터 각 도구를 정확히 무엇이 되고 안 되는지 짚는다.


3장 · Zod — 표준이 된 이유, 그리고 Zod 4의 반격

Zod는 2020년 등장 이래 사실상 TypeScript 검증의 표준이 됐다. 이유 세 가지.

  1. 체이닝 API의 친숙함z.string().email().min(5) 한 줄로 의도가 드러난다. Joi·Yup에서 온 사람에게 학습 비용이 거의 0.
  2. z.infer 타입 추론 — 스키마에서 TS 타입을 자동 도출. 스키마 = 타입의 진실의 원천.
  3. 생태계 폭발 — tRPC·@hookform/resolvers·OpenAI SDK·LangChain·Next.js Server Actions 등 모두 Zod를 일급으로 받는다.

Zod 3 시대의 약점

그래도 Zod는 두 가지 비판을 오래 받았다.

  • 번들 크기 — 체이닝 API 특성상 전체 인덱스가 한 번에 불려 와서 트리 셰이킹이 약했다. 단순 z.string()만 써도 수십 KB가 들어옴.
  • 런타임 비용 — 깊은 객체에서 파싱이 의외로 무거움. 핫 패스(요청당 수천 번 호출)에서 보였다.

Zod 4가 바꾼 것

Zod 4는 2024년 10월 베타 공개, 2025년 본격 GA. 큰 변경 셋.

  • 트리 셰이커블 빌드 — 사용하지 않은 검증자는 번들에서 떨어진다. import { z } from 'zod' 그대로 쓰면서 실제 결과는 작아짐.
  • 파서 재작성 — 인터널을 단순화하고 핫 패스를 최적화. 공식 벤치마크 기준 v3 대비 의미 있는 속도 향상(릴리즈 노트의 수치 참고).
  • 확장 분리zod 코어와 zod/v4, 미니 빌드(zod/v4-mini)가 분리됐다. 후자는 Valibot 수준의 매우 작은 번들을 목표.

Zod로 같은 User 스키마

import { z } from 'zod'

const User = z.object({
  email: z.string().email(),
  age: z.number().int().min(0).max(150),
  name: z.string().min(1),
})

type User = z.infer<typeof User>

const parsed = User.parse({ email: 'foo@example.com', age: 30, name: 'Foo' })
//    ^ parsed: User

safeParse로 에러를 값으로 받기.

const result = User.safeParse(unknown)
if (!result.success) {
  // result.error is a ZodError; pretty issues at result.error.issues
  console.error(result.error.flatten())
}

Zod의 강점 (2026)

  • 가장 큰 생태계. 신규 라이브러리·SDK가 거의 자동으로 Zod 어댑터 제공.
  • 4.x로 번들·속도 약점이 크게 줄었음.
  • 비동기 검증(z.string().refine(async ...)) 지원이 매끄러움.
  • 학습 자료가 압도적.

Zod의 약점 (2026)

  • 절대적 번들은 Valibot보다 여전히 큼. 엣지 워커에서 한 KB라도 줄이고 싶으면 후보가 아님.
  • z.discriminatedUnion 같은 일부 API 학습 비용이 약간 있음.
  • JSON Schema 익스포트는 외부 패키지(@asteasolutions/zod-to-openapi 등). 직접 지원은 부분적.

4장 · Valibot — 함수형 모듈러로 번들을 깎다

Valibot은 2023년 말~2024년 사이 폭발적으로 자란 라이브러리다. 핵심 철학 하나.

"기본을 함수로 짜라. 안 쓰는 코드는 번들에 없다."

Zod가 z.string().email().min(5)라면 Valibot은 함수 합성이다.

import * as v from 'valibot'

const User = v.object({
  email: v.pipe(v.string(), v.email()),
  age: v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(150)),
  name: v.pipe(v.string(), v.minLength(1)),
})

type User = v.InferOutput<typeof User>

const parsed = v.parse(User, { email: 'foo@example.com', age: 30, name: 'Foo' })

v.pipe(...)로 검증자를 합성하고, v.parse/v.safeParse로 적용한다. 함수가 모두 named export — 안 쓴 건 esbuild·Rollup이 떨어낸다.

번들 차이

공식 사이트와 커뮤니티 벤치마크가 보여 주는 그림.

  • v.string() 한 번만 쓰면 Valibot에서 들어가는 코드는 수백 바이트 수준.
  • 같은 코드가 Zod 3에서는 수십 KB. Zod 4에서는 큰 폭으로 줄었지만 Valibot보다는 여전히 큼.
  • 큰 스키마(여러 검증자)를 모두 쓰면 차이는 줄어들지만, 작게 시작할수록 Valibot이 유리.

Valibot의 BaseSchema API

Valibot 1.0 GA(2024) 이후 API가 안정화됐다. 주요 함수.

  • v.object, v.array, v.tuple, v.union, v.intersect, v.record
  • v.string, v.number, v.boolean, v.literal, v.date, v.bigint
  • v.pipe(schema, ...actions) — 검증자 합성
  • v.transform(fn), v.brand(name) — 변환·브랜드 타입

비동기 검증은 별도 v.parseAsync/v.safeParseAsync.

Valibot의 강점

  • 번들 크기 압도적 — 프런트엔드·엣지 어디서나 가장 작음.
  • 타입 추론 품질InferOutput·InferInput 양쪽 다 정확.
  • 함수형이라 합성이 쉬움 — 새 검증자 만드는 비용이 낮음.
  • 빠르게 자라는 생태계 — Standard Schema 지원, @valibot/to-json-schema, React Hook Form @hookform/resolvers/valibot.

Valibot의 약점

  • API 학습 비용 — 체이닝에 익숙한 사람에게 처음엔 어색.
  • 에러 메시지 사용자화는 약간 더 손이 감.
  • Zod 어댑터를 받는 일부 라이브러리에서 Valibot 어댑터는 아직 없거나 늦게 추가됨.
  • OpenAPI 익스포트가 Zod만큼 두꺼운 패키지가 없음 — 직접 짜야 할 일이 더 있음.

5장 · ArkType — TypeScript 문법을 그대로 스키마로

ArkType은 2024년에 v2를 GA로 내며 주목받았다. 발상이 다르다.

"TypeScript 표현을 문자열로 받아서, 그대로 런타임 스키마로 만든다."

같은 User 스키마.

import { type } from 'arktype'

const User = type({
  email: 'string.email',
  age: 'number.integer >= 0',
  name: 'string > 0',
})

// type 추론: TS가 정확히 그 표현을 이해
type User = typeof User.infer

const out = User({ email: 'foo@example.com', age: 30, name: 'Foo' })
if (out instanceof type.errors) {
  console.error(out.summary)
} else {
  // out is the validated value
}

핵심은 'number.integer >= 0' 같은 표현이 TS 컴파일러에 의해 정확한 타입으로 추론된다는 것. ArkType은 자체 미니 DSL을 만들고, 그 DSL을 TS 템플릿 리터럴 타입으로 파싱한다. 결과는 스키마를 손으로 쓴 TS 타입과 100% 일치.

ArkType의 차별점

  • TS 표현 그대로'string | number', '(string | number)[]>=2' 같이 친숙.
  • 타입 우선typeof User.infer로 즉시 사용. 별도 .infer 호출 비용 없음(타입 레벨).
  • JSON Schema 익스포트 빌트인User.toJsonSchema() 한 줄.
  • 런타임 성능 — 컴파일된 검증자 — 스키마 정의 시 내부적으로 검증 함수를 미리 빌드.

ArkType 객체 정의의 다른 모양

문자열만이 아니라 TS 객체 그대로도 받는다.

import { type } from 'arktype'

const Post = type({
  id: 'number.integer',
  title: 'string > 0',
  tags: 'string[]',
  author: {
    id: 'number',
    name: 'string',
  },
  publishedAt: 'Date',
})

ArkType의 강점

  • TS 친화도 최상 — TS를 잘 아는 사람에게 학습 곡선이 거의 0.
  • JSON Schema 빌트인 — 외부 패키지 없이 변환.
  • 런타임 빠름 — 컴파일된 검증자.
  • 함수 호출이 스키마 = User(value) — API가 직관적.

ArkType의 약점

  • 에코시스템이 아직 작다 — tRPC·React Hook Form 어댑터는 있지만 Zod만큼 두껍지 않음.
  • 에러 메시지 한국어화·사용자화 자료가 적음.
  • 비동기 검증 지원이 부분적 — 동기 중심 설계.
  • DSL 문자열에 익숙해져야 함'number.integer >= 0'이 처음엔 마법처럼 느껴짐.

6장 · TypeBox — 만든 게 그대로 JSON Schema

TypeBox는 2021년 등장 이후 Fastify 진영에서 사랑받다가, AI 도구 호출 시대에 들어와 다시 주목받았다.

"내가 만든 스키마가 곧 JSON Schema다. AJV로 컴파일하면 가장 빠른 런타임."

같은 User 스키마.

import { Type, Static } from '@sinclair/typebox'
import { Value } from '@sinclair/typebox/value'

const User = Type.Object({
  email: Type.String({ format: 'email' }),
  age: Type.Integer({ minimum: 0, maximum: 150 }),
  name: Type.String({ minLength: 1 }),
})

type User = Static<typeof User>

const ok = Value.Check(User, unknown)
const errors = [...Value.Errors(User, unknown)]

Type.Object({ ... })가 반환하는 값은 JSON Schema 그 자체. JSON.stringify(User)하면 표준 JSON Schema 문서가 나온다.

AJV 결합 — 가장 빠른 런타임

import Ajv from 'ajv'
import addFormats from 'ajv-formats'
const ajv = addFormats(new Ajv())

const check = ajv.compile(User)
const ok = check(value) // 컴파일된 함수 직접 호출 — 매우 빠름

AJV는 JSON Schema를 받아서 검증 함수를 V8 친화적인 코드로 빌드한다. 결과는 일반적으로 가장 빠른 런타임 검증 중 하나다.

TypeBox + Fastify

import Fastify from 'fastify'

const app = Fastify().withTypeProvider<TypeBoxTypeProvider>()

app.post('/users', {
  schema: { body: User, response: { 200: User } },
}, async (req) => {
  // req.body is typed as User automatically
  return req.body
})

스키마가 곧 OpenAPI 응답 명세 + 런타임 검증 + 응답 직렬화까지. 한 번 정의로 네 가지 일을 한다.

AI 도구 호출에서 TypeBox

OpenAI·Anthropic·Gemini의 함수/도구 정의는 JSON Schema다.

const GetWeather = Type.Object({
  city: Type.String(),
  units: Type.Optional(Type.Union([Type.Literal('c'), Type.Literal('f')])),
})

const tool = {
  type: 'function',
  function: {
    name: 'get_weather',
    description: '주어진 도시의 현재 날씨',
    parameters: GetWeather, // 그대로 JSON Schema
  },
}

zod-to-json-schema 같은 변환기를 거치지 않고 직접 사용 가능. 변환 시 잃어버리는 정보(예: format, description)가 없다.

TypeBox의 강점

  • JSON Schema 표준 그대로 — OpenAPI·AI 도구 호출·AJV·Ajv 모두 직결.
  • AJV로 가장 빠른 런타임 — 핫 패스 검증에서 최적.
  • Fastify·Hono·Express 미들웨어 풍부.
  • 번들 매우 작음Type.* 함수 자체는 가볍다.

TypeBox의 약점

  • API가 약간 verboseType.Object({ x: Type.String() })이 길게 느껴짐.
  • 체이닝의 매력은 없음z.string().email().min(5) 같은 짧음이 그리울 수 있음.
  • 에코시스템이 Fastify 쪽에 치우침 — React 폼 라이브러리 어댑터는 Zod·Valibot보다 적음.
  • 비동기 검증은 약함 — JSON Schema 자체가 동기 검증 중심.

7장 · Effect Schema — Effect 생태계의 검증 도구

Effect는 Scala·Haskell 영향을 받은 풀스택 TS 함수형 프레임워크다. Effect Schema는 그 안의 데이터 검증·변환 모듈.

"검증은 양방향 변환이다. 그리고 모든 에러는 타입에 들어간다."

같은 User 스키마.

import { Schema } from 'effect'

const User = Schema.Struct({
  email: Schema.String.pipe(Schema.email()),
  age: Schema.Number.pipe(Schema.int(), Schema.between(0, 150)),
  name: Schema.NonEmptyString,
})

type User = Schema.Schema.Type<typeof User>

const decode = Schema.decodeUnknownSync(User)
const out = decode({ email: 'foo@example.com', age: 30, name: 'Foo' })

decodeUnknownSync는 동기 검증. decodeUnknown 또는 decode는 Effect 값을 반환 — Effect 풀스택 안에서 합성 가능.

Effect Schema의 차별점

  • 양방향 — decodeencode — JSON → 도메인, 도메인 → JSON 둘 다. Date·BigDecimal·Brand 같은 도메인 타입을 자연스럽게 다룸.
  • 타입드 에러ParseError가 타입에 들어 있어, 에러 처리 누락을 컴파일러가 잡음.
  • Schema 합성Schema.compose, Schema.transform, Schema.filter로 합성.
  • JSON Schema 빌트인JSONSchema.make(User) 한 줄.

양방향 변환 예시

import { Schema } from 'effect'

// JSON string으로 들어오는 Date를 도메인 Date로
const DateFromString = Schema.transform(Schema.String, Schema.Date, {
  decode: (s) => new Date(s),
  encode: (d) => d.toISOString(),
})

const Event = Schema.Struct({
  name: Schema.NonEmptyString,
  at: DateFromString,
})

const decoded = Schema.decodeUnknownSync(Event)({ name: 'Launch', at: '2026-05-14T00:00:00Z' })
//      ^ decoded.at is real Date

Effect Schema의 강점

  • 양방향 검증·변환의 우아함 — 다른 라이브러리에서 별도로 짜야 할 일이 자연스럽게 풀림.
  • 타입드 에러로 에러 처리 누락 차단.
  • Effect 풀스택과 합성 — HTTP·DB·캐시·재시도까지 한 모델.
  • JSON Schema 빌트인.

Effect Schema의 약점

  • Effect를 안 쓰면 학습·번들 비용이 큼 — Effect 코어가 같이 들어옴.
  • 체이닝에 비해 코드가 길어 보임.pipe(Schema.email())이 익숙해지기 전까지 어색.
  • 생태계 — 폼·tRPC 어댑터는 부분적.
  • 러닝 커브 — Effect 자체 학습이 필요. 카테고리 이론·함수형 친화가 필요.

8장 · 같은 스키마, 5가지 라이브러리 — User(email + age + name)

5개를 나란히 본다. 같은 의미의 스키마.

8.1 Zod

import { z } from 'zod'

const User = z.object({
  email: z.string().email(),
  age: z.number().int().min(0).max(150),
  name: z.string().min(1),
})

type User = z.infer<typeof User>

8.2 Valibot

import * as v from 'valibot'

const User = v.object({
  email: v.pipe(v.string(), v.email()),
  age: v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(150)),
  name: v.pipe(v.string(), v.minLength(1)),
})

type User = v.InferOutput<typeof User>

8.3 ArkType

import { type } from 'arktype'

const User = type({
  email: 'string.email',
  age: '0 <= number.integer <= 150',
  name: 'string > 0',
})

type User = typeof User.infer

8.4 TypeBox

import { Type, Static } from '@sinclair/typebox'

const User = Type.Object({
  email: Type.String({ format: 'email' }),
  age: Type.Integer({ minimum: 0, maximum: 150 }),
  name: Type.String({ minLength: 1 }),
})

type User = Static<typeof User>

8.5 Effect Schema

import { Schema } from 'effect'

const User = Schema.Struct({
  email: Schema.String.pipe(Schema.email()),
  age: Schema.Number.pipe(Schema.int(), Schema.between(0, 150)),
  name: Schema.NonEmptyString,
})

type User = Schema.Schema.Type<typeof User>

비교 — 같은 스키마, 다른 철학

라이브러리줄 수문법 스타일추론 호출
Zod짧음체이닝z.infer
Valibot중간함수 합성 (pipe)v.InferOutput
ArkType가장 짧음TS DSL 문자열typeof X.infer
TypeBox중간Type 빌더Static
Effect Schema중간.pipe 체이닝Schema.Schema.Type

읽기 쉬움은 Zod·ArkType이 강하고, 번들·트리 셰이킹은 Valibot·TypeBox가 강하고, 변환·에러 타입까지 챙기면 Effect Schema가 강하다.


9장 · AI 도구 호출 시대 — JSON Schema가 강제된다

2024~2025 사이 가장 큰 흐름. 모든 주요 LLM이 도구 정의를 JSON Schema로 받는다.

9.1 OpenAI function calling

const tools = [{
  type: 'function',
  function: {
    name: 'get_weather',
    description: 'Get the current weather in a given city',
    parameters: {
      type: 'object',
      properties: {
        city: { type: 'string' },
        units: { type: 'string', enum: ['c', 'f'] },
      },
      required: ['city'],
    },
  },
}]

parameters는 표준 JSON Schema. 객체 직접 작성하거나, TS 스키마 라이브러리에서 변환해야 한다.

9.2 Anthropic tool use

const tools = [{
  name: 'get_weather',
  description: 'Get the current weather in a given city',
  input_schema: {
    type: 'object',
    properties: {
      city: { type: 'string' },
      units: { type: 'string', enum: ['c', 'f'] },
    },
    required: ['city'],
  },
}]

input_schema 키만 다를 뿐 같은 모양.

9.3 라이브러리별 JSON Schema 변환

  • Zod@asteasolutions/zod-to-openapi, zod-to-json-schema, OpenAI SDK가 직접 받기도 함(zodFunction 헬퍼). Anthropic SDK도 Zod 직접 지원이 늘었다.
  • Valibot@valibot/to-json-schema 공식 패키지.
  • ArkTypeUser.toJsonSchema() 빌트인.
  • TypeBox — 변환 필요 없음. 정의 자체가 JSON Schema.
  • Effect SchemaJSONSchema.make(User) 빌트인.

9.4 변환 시 잃어버리는 것

JSON Schema는 TS 표현보다 한정적이다. 변환 시 종종 잃는 것들.

  • z.transform/v.transform 같은 양방향 변환 — JSON Schema는 입력 검증만 표현.
  • z.brand로 만든 브랜드 타입 — 단순 string 등으로 평탄화.
  • 세밀한 refine 함수 — JSON Schema에 표현 불가능한 검증은 사라짐.
  • 재귀 스키마$ref 처리 라이브러리·LLM 측이 다 잘 받지 않음. 평탄화하거나 깊이 제한이 안전.

9.5 OpenAI structured outputs와 strict 모드

OpenAI 2024 후반에 등장한 structured outputs는 JSON Schema 일부 제약(예: additionalProperties: false, 필수 필드, $ref 미지원 등)을 강제한다. TS 라이브러리가 만드는 JSON Schema가 그 제약을 깨면 OpenAI 측이 거부한다.

실무 팁.

  • 도구 호출용 스키마는 가능한 한 단순하게(평탄, additionalProperties: false).
  • description 필드를 적극 활용 — LLM이 의미를 이해하는 가장 좋은 단서.
  • 재귀·$ref는 피하거나 LLM 친화적으로 평탄화.
  • 변환 결과를 한 번 sanity check — 변환된 JSON Schema를 직접 보고 의도와 같은지.

10장 · 7축 상세 비교

10.1 번들 크기

순수 코어를 작은 스키마 하나 정도만 쓰는 가정.

라이브러리작은 사용큰 사용비고
Valibot가장 작음 (수백 B)작음트리 셰이킹의 정점
TypeBox매우 작음작음Type.* 자체가 가벼움
ArkType작음중간파서가 함께 들어옴
Zod 4중간 (큰 폭 감소)중간v4-mini는 더 작음
Zod 3트리 셰이킹 약함
Effect SchemaEffect 코어 포함
Yup중간중간
Joi브라우저 친화도 낮음

10.2 TypeScript 추론 품질

라이브러리추론 품질비고
ArkType최상TS 표현 그대로
Zod 4매우 좋음z.infer 광범위
Valibot매우 좋음InferOutput/InferInput
Effect Schema매우 좋음양방향 타입까지
TypeBox좋음Static
Yup보통추론은 약함
Joi약함별도 타입 작성 필요

10.3 JSON Schema 익스포트

라이브러리지원방식
TypeBox네이티브정의 자체
ArkType빌트인.toJsonSchema()
Effect Schema빌트인JSONSchema.make()
Valibot공식 외부@valibot/to-json-schema
Zod외부zod-to-json-schema, @asteasolutions/zod-to-openapi
Yup빈약비공식 변환기
Joi없음직접 작성

10.4 비동기 검증

라이브러리지원패턴
Zod강함z.string().refine(async ...) + parseAsync
Valibot강함parseAsync/safeParseAsync
Effect Schema강함Effect 자체가 비동기
ArkType부분동기 중심
TypeBox약함동기 검증 위주

10.5 OpenAPI 익스포트

라이브러리지원도구
TypeBox매우 좋음Fastify + Type Provider
ArkType좋음.toJsonSchema() 직결
Zod좋음@asteasolutions/zod-to-openapi, @hono/zod-openapi
Effect Schema좋음Effect HTTP·OpenAPI 어댑터
Valibot좋음@valibot/to-json-schema

10.6 런타임 비용 (대략, 핫 패스 기준)

라이브러리검증 속도비고
TypeBox + AJV (compile)가장 빠름V8 친화 컴파일
Valibot매우 빠름함수 합성 단순
ArkType빠름컴파일된 검증자
Zod 4빠름큰 폭 개선
Effect Schema중간~빠름Effect 오버헤드
Zod 3보통깊은 객체 약함
Joi느림무거운 옛 API

(정확한 수치는 케이스마다 다름. 각 라이브러리 공식 벤치마크와 자신의 워크로드에서 측정 권장.)

10.7 생태계 (어댑터·통합)

라이브러리tRPCRHFOpenAI/Anthropic SDKFastifyHonoNext.js
Zod일급일급일급가능가능일급
Valibot지원일급 (@hookform/resolvers/valibot)외부 변환가능가능가능
ArkType지원지원외부 변환가능가능가능
TypeBox부분부분직접일급일급 (@hono/typebox-validator)가능
Effect Schema부분부분외부 변환가능가능가능

11장 · Yup·Joi·Superstruct — 살아 있지만

11.1 Yup

Formik 시대의 표준. 폼 검증에서 여전히 쓰이지만, 신규 채택은 줄어든다. Zod·Valibot에 비해 TS 추론이 약하고 번들이 무거움.

import * as yup from 'yup'

const User = yup.object({
  email: yup.string().email().required(),
  age: yup.number().integer().min(0).max(150).required(),
  name: yup.string().min(1).required(),
})

11.2 Joi

Node 백엔드의 옛 표준. 브라우저 친화도 낮음(번들 큼, 의존성 깊음). 서버 검증에서 레거시 코드베이스에 많이 남아 있다.

const Joi = require('joi')
const User = Joi.object({
  email: Joi.string().email().required(),
  age: Joi.number().integer().min(0).max(150).required(),
  name: Joi.string().min(1).required(),
})

11.3 Superstruct

작고 가벼운 검증. io-ts보다 친절하지만, Zod·Valibot에 비하면 에코시스템이 작다. 작은 프로젝트·라이브러리 내부 검증에는 여전히 좋은 선택.

import { object, string, number, refine } from 'superstruct'

const Email = refine(string(), 'email', (v) => /.+@.+/.test(v))
const User = object({
  email: Email,
  age: number(),
  name: string(),
})

11.4 io-ts

전통 함수형 진영. Effect Schema가 그 자리를 빠르게 가져가고 있다. 새 코드에서 io-ts를 자발적으로 시작하는 경우는 줄어든다.


12장 · Standard Schema — 라이브러리 간 호환

2024년 말~2025년 사이 등장한 Standard Schema 스펙은 검증 라이브러리들이 합의한 최소 인터페이스다. tRPC·Hono·React Hook Form 같은 도구가 한 어댑터만으로 Zod·Valibot·ArkType을 받게 하려는 시도.

핵심 인터페이스(간단화).

interface StandardSchemaV1<Input = unknown, Output = Input> {
  '~standard': {
    version: 1
    vendor: string
    validate: (value: unknown) => StandardSchemaV1.Result<Output> | Promise<StandardSchemaV1.Result<Output>>
    types?: { input: Input; output: Output }
  }
}

Zod·Valibot·ArkType·Effect Schema 모두 이 인터페이스를 구현한다. 결과 — 라이브러리 골라잡기 비용이 더 낮아졌다. 한 코드베이스에 여러 라이브러리가 공존하거나, 다른 도구로 갈아탈 때의 비용이 줄었다.

다만 Standard Schema는 최소 공통일 뿐 라이브러리 고유 기능(transform, refine, brand 등)은 어댑터 작성자가 해야 할 일이 남는다.


13장 · 무엇을 언제 골라야 하나 — 솔직한 결론

자, 정답은 항상 그렇듯 "상황 따라 다르다". 다만 패턴이 있다.

Zod 4를 고를 때

  • 가장 큰 생태계가 필요. tRPC·Next.js·React Hook Form·OpenAI/Anthropic SDK 모두 일급.
  • 학습 자료·튜토리얼·답변 풀(Stack Overflow)이 가장 두꺼움.
  • 번들 한 KB의 차이가 중요하지 않은 백엔드·풀스택.
  • 팀에 Zod 경험자가 이미 있음.

Valibot을 고를 때

  • 프런트엔드·엣지 우선. 번들이 곧 비용.
  • 함수형 합성을 선호.
  • React Hook Form·React Router·Next.js 클라이언트 폼 검증.
  • "Zod와 거의 같은 일을 더 작게" — 명확한 동기.

ArkType을 고를 때

  • TS 추론 품질이 최상이어야 함.
  • TS 표현을 그대로 스키마로 쓰는 사고가 자연스러움.
  • JSON Schema 빌트인이 필요(외부 패키지 의존 회피).
  • 자체 미니 DSL에 적응할 수 있는 팀.

TypeBox를 고를 때

  • API 백엔드 — Fastify·Hono·Express + OpenAPI.
  • AI 도구 호출 — JSON Schema를 직접 LLM에 보냄.
  • 핫 패스 검증 — AJV로 가장 빠른 런타임.
  • 스키마 = OpenAPI = 검증 = 직렬화. 한 정의 네 가지 일.

Effect Schema를 고를 때

  • 팀이 이미 Effect 풀스택을 가는 중.
  • 양방향 인코딩·변환이 핵심(Date·Brand·BigDecimal 등).
  • 타입드 에러를 컴파일러로 강제하고 싶음.
  • 함수형·카테고리 친화 팀.

Yup·Joi·Superstruct를 고를 때

  • 레거시 코드베이스 유지보수.
  • Yup — Formik 기반 코드.
  • Joi — Hapi.js 백엔드.
  • Superstruct — 의존성 최소 작은 라이브러리.

안티 체크리스트 (피해야 할 패턴)

  • 한 코드베이스에 검증 라이브러리 3개 — Standard Schema가 있어도 학습·디버그 비용은 누적.
  • "Valibot이 더 작아서 Zod에서 갈아탔다"는 결정을 측정 없이 — 큰 스키마에선 차이가 줄어든다.
  • TypeBox로 모든 폼 검증 — 폼 라이브러리 어댑터가 빈약. Zod·Valibot이 자연스러움.
  • AI 도구 호출에 Zod 그대로 보내기 — 변환을 거치되, 변환 후 JSON Schema를 한 번 점검.
  • z.refine/v.check로 잘 안 변환되는 검증을 잔뜩 — 변환 후 그 검증은 사라짐. 핵심 검증은 변환 가능한 형태로.

에필로그 — 스키마는 API 경계의 진실

검증 라이브러리 선택은 얼마나 작은가, 얼마나 친숙한가, 얼마나 멀리 갈 수 있는가의 트레이드오프다. 한쪽이 우월하지 않다. 다만 2026년 5월의 풍경은 분명하다.

  • JSON Schema 호환이 1군 자격이 됐다 — AI 도구 호출이 강제했다. Zod도 외부 패키지로 따라잡았고, TypeBox·ArkType·Effect Schema는 빌트인.
  • 번들 크기 경쟁이 끝나지 않았다 — Valibot이 정점을 보였고, Zod 4가 큰 폭으로 따라왔다. 차이는 작아지지만 여전히 의미 있는 차이.
  • TS 추론은 표준 자격 — 어떤 도구도 추론이 약하면 신규 채택에서 떨어진다.
  • Standard Schema로 갈아타기 비용이 낮아졌다 — 더 이상 "한 번 결정하면 평생"이 아님.
  • 풀스택 한 스키마가 점점 보편 — 같은 스키마로 서버 검증·클라이언트 폼·문서·AI 도구 호출까지.

결정 체크리스트

  • API 경계가 최우선인가? — TypeBox 또는 Zod + zod-to-openapi.
  • 폼·번들·엣지가 최우선인가? — Valibot 또는 Zod 4-mini.
  • TS 추론 품질이 최상이어야 하나? — ArkType.
  • AI 도구 호출 JSON Schema가 중심인가? — TypeBox 또는 ArkType.
  • Effect 풀스택 중인가? — Effect Schema.
  • 학습·튜토리얼·생태계 두께가 중요? — Zod.
  • 비동기 검증이 일상? — Zod·Valibot·Effect Schema.
  • 양방향 변환·브랜드 타입이 필요? — Effect Schema 또는 Zod transform.

안티 패턴

  1. 측정 없이 라이브러리 갈아타기 — 결정은 측정 후.
  2. AI 도구에 변환 결과를 보지 않고 보내기 — additionalProperties·required 누락이 잦음.
  3. JSON Schema로 표현 못 하는 refine을 핵심 검증으로 — 변환 시 사라짐.
  4. 폼 검증에 TypeBox — RHF 어댑터가 약함. Zod·Valibot이 자연스러움.
  5. 큰 객체에 매 요청마다 z.parse 반복 — 핫 패스라면 컴파일된 검증자(TypeBox + AJV) 고려.
  6. 한 스키마 두 진실의 원천 — DB 스키마와 검증 스키마가 다른 곳에 정의되어 미스매치.
  7. 에러 메시지 사용자화 안 하기 — 라이브러리 기본 에러 메시지가 사용자에게 노출됨.

다음 글 예고

다음 글 후보: AI 도구 호출 JSON Schema 디버깅 — OpenAI structured outputs·Anthropic tool use 실수 모음, Zod 4 마이그레이션 후기 — 코드베이스 5만 줄 한 달 작업, Valibot 깊게 — 트리 셰이커가 실제로 어떻게 떨어내는가.

"검증은 코드의 경계다. 경계가 명확한 코드만큼 멀리 간다."

— TypeScript 데이터 검증 2026, 끝.


참고 / References