--- id: wiki-2026-0508-zod title: Zod category: 10_Wiki/Topics status: verified canonical_id: self aliases: [Zod Schema, Zod 4, ts-schema-validation] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [typescript, validation, schema, runtime-types] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: typescript framework: zod --- # Zod ## 매 한 줄 > **"매 schema 한 번 정의 매 runtime validation + static type 매 동시"**. 매 Colin McDonnell 매 2020 출시 매 TypeScript-first schema validator — 매 2026 매 Zod 4 매 stable, tRPC / OpenAI SDK / Vercel AI SDK / Next.js Server Actions 매 사실상 standard. 매 대안: Valibot (smaller bundle), ArkType (faster). ## 매 핵심 ### 매 핵심 idea - 매 single source of truth: schema 의 의 매 type 도 (`z.infer<>`) 도 의 의. - 매 parse → validated value (typed); failure → `ZodError` (or `safeParse` returns Result-like). - Tree-shakeable, zero deps. ### 매 주요 method - `z.object`, `z.array`, `z.union`, `z.discriminatedUnion`, `z.record`, `z.tuple`. - `.refine`, `.transform`, `.pipe`, `.brand`. - `.optional`, `.nullable`, `.nullish`, `.default`, `.catch`. - `z.infer`, `z.input`, `z.output`. ### 매 응용 1. tRPC: input/output schema → end-to-end type safety. 2. React Hook Form + `zodResolver`. 3. OpenAI structured outputs / function calling: schema → JSON Schema. 4. Server Actions / API route input validation. 5. Env variable validation (zod-env, t3-env). ## 💻 패턴 ### Basic schema + infer ```ts import { z } from "zod"; const User = z.object({ id: z.string().uuid(), email: z.string().email(), age: z.number().int().min(0).max(150), role: z.enum(["admin", "user", "guest"]), }); type User = z.infer; const result = User.safeParse(input); if (!result.success) console.error(result.error.flatten()); else console.log(result.data); // typed ``` ### Discriminated union ```ts const Event = z.discriminatedUnion("type", [ z.object({ type: z.literal("click"), x: z.number(), y: z.number() }), z.object({ type: z.literal("keydown"), key: z.string() }), z.object({ type: z.literal("scroll"), delta: z.number() }), ]); type Event = z.infer; // narrow 매 자동 ``` ### Refine + transform + pipe ```ts const Password = z.string() .min(8) .refine((s) => /[A-Z]/.test(s) && /\d/.test(s), "Need uppercase + digit"); const TrimmedEmail = z.string() .transform((s) => s.trim().toLowerCase()) .pipe(z.string().email()); ``` ### Recursive schema (Zod 4) ```ts type Comment = { text: string; replies: Comment[] }; const Comment: z.ZodType = z.lazy(() => z.object({ text: z.string(), replies: z.array(Comment) }), ); ``` ### Branded types (nominal) ```ts const UserId = z.string().uuid().brand<"UserId">(); type UserId = z.infer; function loadUser(id: UserId) { /* … */ } loadUser(crypto.randomUUID() as any); // 매 must parse 먼저 ``` ### Form integration (React Hook Form) ```tsx import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; const Schema = z.object({ email: z.string().email(), age: z.coerce.number().min(18) }); type Form = z.infer; function SignUp() { const { register, handleSubmit, formState: { errors } } = useForm
({ resolver: zodResolver(Schema), }); return ( console.log(d))}> {errors.email && {errors.email.message}}
); } ``` ### Env validation ```ts // env.ts — process.env 매 typed import { z } from "zod"; export const env = z.object({ NODE_ENV: z.enum(["development", "production", "test"]), DATABASE_URL: z.string().url(), OPENAI_API_KEY: z.string().startsWith("sk-"), PORT: z.coerce.number().default(3000), }).parse(process.env); ``` ### LLM structured output ```ts import OpenAI from "openai"; import { zodResponseFormat } from "openai/helpers/zod"; const Recipe = z.object({ name: z.string(), ingredients: z.array(z.object({ item: z.string(), qty: z.string() })), steps: z.array(z.string()), }); const openai = new OpenAI(); const r = await openai.chat.completions.parse({ model: "gpt-5", messages: [{ role: "user", content: "Recipe for cacio e pepe" }], response_format: zodResponseFormat(Recipe, "recipe"), }); const recipe = r.choices[0].message.parsed!; // typed ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | TS-only project | Zod (default) | | Bundle size critical (<5KB) | Valibot | | Performance critical (10M validations/s) | ArkType | | JSON Schema 의 매 first-class | TypeBox | | Java / Python interop schema | JSON Schema + ajv / pydantic | **기본값**: Zod 4. 매 모든 boundary (network, env, storage) 매 parse. ## 🔗 Graph - 부모: [[Schema Validation]] - 변형: [[Valibot]] · [[ArkType]] · [[Yup]] - 응용: [[React Hook Form]] · [[Server Actions]] - Adjacent: [[TypeScript]] · [[Branded Types]] · [[JSON Schema]] ## 🤖 LLM 활용 **언제**: API boundary parsing, env validation, LLM output parsing, form schema. **언제 X**: Hot path (>1M ops/s) 매 ArkType 고려, embedded / no TS. ## ❌ 안티패턴 - **Inferring on `.parse` not on schema**: `z.infer` 매 사용. - **`any` cast 후 parse skip**: 매 boundary 매 항상 parse. - **Massive nested `.refine` chains**: 매 readability — `.pipe` 분리. - **Re-parsing same data multiple times**: 매 once at boundary, 매 그 후 typed. - **Throwing in nested transforms**: `z.NEVER` + ctx.addIssue 사용. - **Optional vs nullable 혼동**: optional = `| undefined`, nullable = `| null`. ## 🧪 검증 / 중복 - Verified (Zod 4 official docs, colinhacks/zod GitHub, tRPC/Vercel AI SDK integration). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — Zod 4, schema patterns, LLM structured output integration |