Skip to content

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

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
원문 렌더가 준비되기 전까지 텍스트 가이드로 표시합니다.

프롤로그 — 모든 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 검증의 표준이 됐다. 이유 세 가지.

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 스키마

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은 함수 합성이다.

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 스키마.

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 객체 그대로도 받는다.

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 스키마.

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 결합 — 가장 빠른 런타임

const ajv = addFormats(new Ajv())

const check = ajv.compile(User)

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

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

TypeBox + 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 스키마.

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)` 한 줄.

양방향 변환 예시

// 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

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

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

const User = type({

email: 'string.email',

age: '0 <= number.integer <= 150',

name: 'string > 0',

})

type User = typeof User.infer

8.4 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

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 추론이 약하고 번들이 무거움.

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에 비하면 에코시스템이 작다. 작은 프로젝트·라이브러리 내부 검증에는 여전히 좋은 선택.

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

- [Zod 공식](https://zod.dev/)

- [Zod GitHub — colinhacks/zod](https://github.com/colinhacks/zod)

- [Zod v4 릴리즈 노트](https://zod.dev/v4)

- [Valibot 공식](https://valibot.dev/)

- [Valibot GitHub — fabian-hiller/valibot](https://github.com/fabian-hiller/valibot)

- [`@valibot/to-json-schema`](https://valibot.dev/guides/json-schema/)

- [ArkType 공식](https://arktype.io/)

- [ArkType GitHub — arktypeio/arktype](https://github.com/arktypeio/arktype)

- [TypeBox 공식 README](https://github.com/sinclairzx81/typebox)

- [TypeBox + Fastify Type Provider](https://fastify.dev/docs/latest/Reference/Type-Providers/)

- [Effect 공식](https://effect.website/)

- [Effect Schema 문서](https://effect.website/docs/schema/introduction)

- [Standard Schema 공식](https://standardschema.dev/)

- [Standard Schema GitHub](https://github.com/standard-schema/standard-schema)

- [AJV — JSON Schema 검증기](https://ajv.js.org/)

- [`zod-to-json-schema`](https://github.com/StefanTerdell/zod-to-json-schema)

- [`@asteasolutions/zod-to-openapi`](https://github.com/asteasolutions/zod-to-openapi)

- [`@hono/zod-openapi`](https://github.com/honojs/middleware/tree/main/packages/zod-openapi)

- [`@hookform/resolvers`](https://github.com/react-hook-form/resolvers)

- [OpenAI function calling 문서](https://platform.openai.com/docs/guides/function-calling)

- [OpenAI structured outputs 발표](https://openai.com/index/introducing-structured-outputs-in-the-api/)

- [Anthropic tool use 문서](https://docs.anthropic.com/en/docs/build-with-claude/tool-use)

- [Yup 공식](https://github.com/jquense/yup)

- [Joi 공식](https://joi.dev/)

- [Superstruct 공식](https://docs.superstructjs.org/)

- [io-ts 공식](https://github.com/gcanti/io-ts)

- [tRPC 공식](https://trpc.io/)

- [React Hook Form](https://react-hook-form.com/)

현재 단락 (1/489)

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

작성 글자: 0원문 글자: 19,125작성 단락: 0/489