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

- Name
- Youngju Kim
- @fjvbn20031
프롤로그 — 모든 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, Joi | z.string().email() 식. 가장 친숙. |
| 모듈러 함수 | Valibot | string([email()]). 트리 셰이킹 친화. |
| TS 문법을 스키마로 | ArkType | TS 타입 표현을 그대로 런타임에. |
| JSON Schema 네이티브 | TypeBox | 만든 게 곧 JSON Schema. |
| 함수형 + Effect | Effect 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 4 | Valibot | ArkType | TypeBox | Effect Schema |
|---|---|---|---|---|---|
| 번들 (코어) | 중간 | 매우 작음 (모듈러) | 작음 | 매우 작음 | 큼(Effect 포함) |
| TS 추론 품질 | 매우 좋음 | 매우 좋음 | 최상 (TS 표현 그대로) | 좋음 | 매우 좋음 |
| JSON Schema 익스포트 | 외부 패키지 필요 | 외부 패키지 필요 | 빌트인 | 빌트인 (정의 자체가 JSON Schema) | 빌트인 |
| 비동기 검증 | O | O | 부분 | 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 검증의 표준이 됐다. 이유 세 가지.
- 체이닝 API의 친숙함 —
z.string().email().min(5)한 줄로 의도가 드러난다. Joi·Yup에서 온 사람에게 학습 비용이 거의 0. z.infer타입 추론 — 스키마에서 TS 타입을 자동 도출. 스키마 = 타입의 진실의 원천.- 생태계 폭발 — 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.recordv.string,v.number,v.boolean,v.literal,v.date,v.bigintv.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가 약간 verbose —
Type.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의 차별점
- 양방향 —
decode↔encode— 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공식 패키지. - ArkType —
User.toJsonSchema()빌트인. - TypeBox — 변환 필요 없음. 정의 자체가 JSON Schema.
- Effect Schema —
JSONSchema.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 Schema | 큼 | 큼 | Effect 코어 포함 |
| 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 생태계 (어댑터·통합)
| 라이브러리 | tRPC | RHF | OpenAI/Anthropic SDK | Fastify | Hono | Next.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.
안티 패턴
- 측정 없이 라이브러리 갈아타기 — 결정은 측정 후.
- AI 도구에 변환 결과를 보지 않고 보내기 —
additionalProperties·required누락이 잦음. - JSON Schema로 표현 못 하는
refine을 핵심 검증으로 — 변환 시 사라짐. - 폼 검증에 TypeBox — RHF 어댑터가 약함. Zod·Valibot이 자연스러움.
- 큰 객체에 매 요청마다
z.parse반복 — 핫 패스라면 컴파일된 검증자(TypeBox + AJV) 고려. - 한 스키마 두 진실의 원천 — DB 스키마와 검증 스키마가 다른 곳에 정의되어 미스매치.
- 에러 메시지 사용자화 안 하기 — 라이브러리 기본 에러 메시지가 사용자에게 노출됨.
다음 글 예고
다음 글 후보: AI 도구 호출 JSON Schema 디버깅 — OpenAI structured outputs·Anthropic tool use 실수 모음, Zod 4 마이그레이션 후기 — 코드베이스 5만 줄 한 달 작업, Valibot 깊게 — 트리 셰이커가 실제로 어떻게 떨어내는가.
"검증은 코드의 경계다. 경계가 명확한 코드만큼 멀리 간다."
— TypeScript 데이터 검증 2026, 끝.
참고 / References
- Zod 공식
- Zod GitHub — colinhacks/zod
- Zod v4 릴리즈 노트
- Valibot 공식
- Valibot GitHub — fabian-hiller/valibot
@valibot/to-json-schema- ArkType 공식
- ArkType GitHub — arktypeio/arktype
- TypeBox 공식 README
- TypeBox + Fastify Type Provider
- Effect 공식
- Effect Schema 문서
- Standard Schema 공식
- Standard Schema GitHub
- AJV — JSON Schema 검증기
zod-to-json-schema@asteasolutions/zod-to-openapi@hono/zod-openapi@hookform/resolvers- OpenAI function calling 문서
- OpenAI structured outputs 발표
- Anthropic tool use 문서
- Yup 공식
- Joi 공식
- Superstruct 공식
- io-ts 공식
- tRPC 공식
- React Hook Form