개요
Zod는 TypeScript 우선의 스키마 검증 라이브러리로, 정의에서 정적 타입을 자동으로 추론할 수 있습니다. 간결하고 불변적인 API를 제공하여 강력한 타입 안전성으로 신뢰할 수 없는 데이터를 파싱하고 검증하며, 제로 의존성으로 JSON Schema를 지원하여 Node.js와 최신 브라우저에서 사용할 수 있습니다.
README 미리보기
\n \n Zod\n \n TypeScript-first schema validation with static type inference\n \n by @colinhacks\n \n\n\n\n\n\n\n\n\n\n\n\n\n Docs\n • \n Discord\n • \n 𝕏\n • \n Bluesky\n \n\n\n\n\n\n### [Read the docs →](https://zod.dev/api)\n\n\n\n\n## What is Zod?\n\nZod is a TypeScript-first validation library. Define a schema and parse some data with it. You'll get back a strongly typed, validated result.\n\n```ts\nimport * as z from "zod";\n\nconst User = z.object({\n name: z.string(),\n});\n\n// some untrusted data...\nconst input = {\n /* stuff */\n};\n\n// the parsed result is validated and type safe!\nconst data = User.parse(input);\n\n// so you can use it with confidence :)\nconsole.log(data.name);\n```\n\n\n\n## Features\n\n- Zero external dependencies\n- Works in Node.js and all modern browsers\n- Tiny: `2kb` core bundle (gzipped)\n- Immutable API: methods return a new instance\n- Concise interface\n- Works with TypeScript and plain JS\n- Built-in JSON Schema conversion\n- Extensive ecosystem\n\n\n\n## Installation\n\n```sh\nnpm install zod\n```\n\n\n\n## Basic usage\n\nBefore you can do anything else, you need to define a schema. For the purposes of this guide, we'll use a simple object schema.\n\n```ts\nimport * as z from "zod";\n\nconst Player = z.object({\n username: z.string(),\n xp: z.number(),\n});\n```\n\n### Parsing data\n\nGiven any Zod schema, use `.parse` to validate an input. If it's valid, Zod returns a strongly-typed _deep clone_ of the input.\n\n```ts\nPlayer.parse({ username: "billie", xp: 100 });\n// => returns { username: "billie", xp: 100 }\n```\n\n**Note** — If your schema uses certain asynchronous APIs like `async` [refinements](https://zod.dev/api#refinements) or [transforms](https://zod.dev/api#transforms), you'll need to use the `.parseAsync()` method instead.\n\n```ts\nconst schema = z.string().refine(async (val) => val.length "hello"\n```\n\n### AOFAQ (5)
문제 해결Zod safeParse의 error.message가 유효성 검사 오류 메시지 대신 JSON 객체를 반환하는 이유는 무엇인가요?
Zod 4에서는 단일 ZodError가 여러 오류를 포함할 수 있기 때문에 error.message는 모든 유효성 검사 이슈를 포함하는 JSON 문자열입니다. 첫 번째 사람이 읽기 쉬운 메시지를 가져오려면 다음을 사용하세요:
const parsedData = signupSchema.safeParse(req.body);
if (!parsedData.success) {
const message = parsedData.error.issues[0]?.message;
return errorResponse(res, 400, message);
}
모든 오류 메시지를 가져오려면:
const messages = parsedData.error.issues.map(issue => issue.message);
전체 오류를 읽기 쉬운 텍스트로 포맷하려면 다음을 사용하세요:
z.prettifyError(parsedData.error);문제 해결Zod v4에서 `.refine()` 타입 프레디케이트가 더 이상 추론된 타입을 좁히지 않는 문제는 어떻게 해결하나요?
.refine()에서 타입 프레디케이트 지원이 Zod **v4.2.2**(PR #5575)에서 복원되었습니다. 먼저 업그레이드하세요:
npm install zod@^4.2.2
기존 v3 패턴이 출력 타입을 다시 좁혀줄 것입니다:
const mySchema = z.string().refine(
(val): val is "specific-value" => checkExternalLogic(val)
);
type MyType = z.infer<typeof mySchema>; // "specific-value"
이전 v4 릴리스에서 업그레이드할 수 없는 경우, 간단한 프레디케이트 기반 타입 좁히기에는 z.custom<T>()를 사용하세요:
const mySchema = z.custom<"specific-value">(
(val) => typeof val === "string" && checkExternalLogic(val),
{ message: "Expected specific-value" }
);
z.custom()은 유효성 검사 ctx를 받지 않으므로 여러 개의 커스텀 이슈를 추가할 수 없습니다. 이전 v4 버전에서 커스텀 오류 보고가 필요하다면 .check() 또는 .transform()을 컨텍스트 API와 함께 사용하고 출력 타입을 명시적으로 단언/좁히세요. 권장되는 해결 방법은 Zod 4.2.2 이상으로 업그레이드하는 것입니다.
문제 해결Zod와 TypeScript 7.1 nightly에서 `.catchall(z.json())`을 사용할 때 발생하는 TS5115 'infinitely circular' 오류를 어떻게 수정하나요?
PR #6645가 포함된 릴리스로 Zod를 업그레이드하세요 (npm install zod@latest 또는 yarn add zod@latest 실행). 이 오류는 TypeScript 7.1 개발 빌드와 함께 사용되는 Zod 4.5.4 및 4.6.5에 영향을 미치며, TypeScript 7.0.2는 영향을 받지 않습니다. 즉시 업그레이드할 수 없는 경우, TypeScript 7.0.2를 계속 사용하거나 v4/core/schemas.d.ts 및 .d.cts에서 $ZodRecordInternals를 패치하여 타입 인수 없이 $ZodTypeInternals를 확장하고 output과 input을 직접 선언하도록 하세요:
export interface $ZodRecordInternals<
Key extends $ZodRecordKey = $ZodRecordKey,
Value extends SomeType = $ZodType,
> extends $ZodTypeInternals {
def: $ZodRecordDef<Key, Value>;
isst: errors.$ZodIssueInvalidType | errors.$ZodIssueInvalidKey<Record<PropertyKey, unknown>>;
optin?: "optional" | undefined;
optout?: "optional" | undefined;
output: $InferZodRecordOutput<Key, Value>;
input: $InferZodRecordInput<Key, Value>;
}문제 해결왜 esbuild에서는 `import { z } from 'zod'`가 `import * as z`보다 4–5배 더 크며, Zod v4 번들은 어떻게 줄일 수 있나요?
이 문제는 Zod v4에 영향을 줍니다 (zod@4.6.4 및 esbuild@0.28.2로 재현 확인됨). Zod는 네임스페이스를 export { z, z as default }로 내보냅니다. Rollup과 Webpack은 이 재수출을 tree-shake할 수 있지만, esbuild는 재수출된 네임스페이스 객체를 불투명하게 취급하므로 import { z } from 'zod'와 default import 모두 약 736KB를 그대로 유지합니다. 네임스페이스 import를 직접 사용하세요:
import * as z from 'zod';
z.object({ x: z.string() });
재현 예제에서 이 방법은 출력 크기를 736KB에서 약 165KB로 줄여줍니다. esbuild에서는 다음 형태를 피하세요:
import { z } from 'zod';
import z from 'zod';
개별 함수 단위의 import도 tree-shake될 수 있습니다:
import { object, string } from 'zod';
object({ x: string() });
@tanstack/ai와 같은 서드파티 의존성이 내부적으로 import { z } from 'zod'를 사용하는 경우, patch-package로 해당 의존성을 패치하거나(또는 fork/resolution을 유지하여) import * as z from 'zod'로 변경하세요. Rollup을 사용하는 Vite 프로덕션 빌드는 같은 방식으로 영향을 받지 않습니다. 이 차이는 주로 esbuild가 번들링을 수행할 때, 예를 들어 esbuild를 직접 사용하거나 Vite의 개발 서버를 사용할 때 나타납니다. 메타파일로 결과를 확인하세요:
npx esbuild app.ts --bundle --platform=node --format=esm --outfile=out.mjs --metafile=meta.json문제 해결왜 제 exhaustive Zod v4 visitor가 $ZodFunction과 $ZodPrefault를 놓치나요?
core 문서가 문서화된 $ZodTypes 유니온에서 이 멤버들을 잘못 누락했습니다. packages/zod/src/v4/core/schemas.ts의 공식 정의에는 $ZodFunction과 $ZodPrefault가 모두 포함되어 있습니다. $ZodFunction과 $ZodPrefault를 처리하도록 exhaustive visitor 또는 타입 레벨 switch를 업데이트하거나, 문서화된 유니온을 복사하는 대신 소스 $ZodTypes에서 케이스를 도출하세요. 문서 수정에서는 packages/docs/content/packages/core.mdx의 $ZodTypes에 두 타입을 모두 추가하고 상속 다이어그램에 $ZodFunction을 추가합니다.