--- id: wiki-2026-0508-zod-런타임-유효성-검사-통합 title: Zod 런타임 유효성 검사 통합 category: 10_Wiki/Topics status: verified canonical_id: self aliases: [Zod, TypeScript runtime validation, Zod schema] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [typescript, validation, zod, runtime-types, schema] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: TypeScript framework: Zod 3.x --- # Zod 런타임 유효성 검사 통합 ## 매 한 줄 > **"매 type schema = single source of truth"**. Zod는 매 TypeScript type을 런타임 schema 로 정의하고, parse 시점에 매 validation + type narrowing 을 동시 제공한다. API boundary, env vars, form input 매 unsafe input 의 매 첫 검증선. ## 매 핵심 ### 매 동기 (Why Zod over alternatives) - **TypeScript types are erased**: 매 컴파일 후 `interface User`는 매 사라짐 → API response 의 `data as User` 매 lying. - **Zod = schema → type**: `z.infer` 로 매 schema 가 source of truth. - **Composability**: 매 `.merge`, `.partial`, `.extend`, `.transform` 으로 매 schema 합성. - **Error-rich**: parse failure 시 매 path, code, message tree 반환. ### 매 경쟁 라이브러리 - **Yup**: 매 older, schema → type 약함, 매 Zod 가 대체. - **io-ts**: 매 더 functional (fp-ts), 매 learning curve 높음. - **Valibot**: 매 tree-shakable, 매 bundle size 우선이면 고려 (~10x smaller). - **ArkType**: 매 string-based syntax, 매 빠르지만 ecosystem 작음. - **Zod**: 매 default choice in 2026 — DX, ecosystem (tRPC, React Hook Form), maturity. ### 매 응용 1. **API boundary**: fetch response 매 parse 후 typed 으로 사용. 2. **Form validation**: React Hook Form + zodResolver. 3. **env vars**: `process.env` 의 매 schema parse, missing key 시 즉시 fail. 4. **DB row → domain**: ORM 결과 매 `Schema.parse(row)`. 5. **LLM structured output**: Claude/GPT JSON response 매 schema 로 검증. ## 💻 패턴 ### Pattern 1: 기본 schema + type inference ```typescript import { z } from "zod"; export const UserSchema = z.object({ id: z.string().uuid(), email: z.string().email(), age: z.number().int().min(0).max(150), role: z.enum(["admin", "user", "guest"]), createdAt: z.coerce.date(), }); export type User = z.infer; // usage const raw: unknown = await fetchUser(); const user = UserSchema.parse(raw); // throws ZodError if invalid // ^? User ``` ### Pattern 2: safeParse for non-throw ```typescript const result = UserSchema.safeParse(raw); if (!result.success) { console.error(result.error.flatten()); return; } const user = result.data; // typed ``` ### Pattern 3: env validation (fail fast at boot) ```typescript const EnvSchema = z.object({ DATABASE_URL: z.string().url(), PORT: z.coerce.number().int().positive().default(3000), NODE_ENV: z.enum(["development", "production", "test"]), ANTHROPIC_API_KEY: z.string().startsWith("sk-ant-"), }); export const env = EnvSchema.parse(process.env); // process exits at startup if invalid — better than runtime surprise ``` ### Pattern 4: discriminated union ```typescript const ResultSchema = z.discriminatedUnion("status", [ z.object({ status: z.literal("ok"), data: z.string() }), z.object({ status: z.literal("error"), code: z.number() }), ]); type Result = z.infer; // narrowing on .status works correctly ``` ### Pattern 5: transform for parse-not-validate ```typescript const DateSchema = z.string().transform((s, ctx) => { const d = new Date(s); if (isNaN(d.getTime())) { ctx.addIssue({ code: "custom", message: "Invalid date" }); return z.NEVER; } return d; }); const out = DateSchema.parse("2026-05-10"); // Date instance ``` ### Pattern 6: API client with Zod ```typescript async function fetchUser(id: string): Promise { const res = await fetch(`/api/users/${id}`); const json = await res.json(); return UserSchema.parse(json); // unknown → User } ``` ### Pattern 7: tRPC integration ```typescript import { router, procedure } from "./trpc"; export const userRouter = router({ create: procedure .input(UserSchema.omit({ id: true, createdAt: true })) .mutation(async ({ input }) => { // input is fully typed + validated return db.user.create({ data: input }); }), }); ``` ### Pattern 8: React Hook Form + Zod ```typescript import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; const FormSchema = z.object({ email: z.string().email(), password: z.string().min(8), }); const { register, handleSubmit, formState } = useForm({ resolver: zodResolver(FormSchema), }); ``` ### Pattern 9: schema composition ```typescript const Base = z.object({ id: z.string(), createdAt: z.date() }); const Post = Base.extend({ title: z.string(), body: z.string() }); const PostUpdate = Post.partial().required({ id: true }); ``` ### Pattern 10: refinement (custom rules) ```typescript const PasswordSchema = z .object({ password: z.string(), confirm: z.string() }) .refine((d) => d.password === d.confirm, { message: "Passwords do not match", path: ["confirm"], }); ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | API boundary, untrusted input | Zod parse | | Internal pure-TS code | Type only, no Zod | | Bundle size critical (mobile, edge) | Valibot | | Functional ergonomics | io-ts | | LLM structured output (Claude/GPT) | Zod + tool schema | | Performance hot path (>10k parses/sec) | Compile to TypeBox/AJV | **기본값**: Zod 3.x — 매 modern TS app 의 default validation layer. ## 🔗 Graph - 부모: [[TypeScript]] · [[Runtime Type Validation]] - 변형: [[Valibot]] · [[Yup]] · [[ArkType]] · [[io-ts]] - 응용: [[tRPC]] · [[React Hook Form]] · [[env validation]] - Adjacent: [[JSON Schema]] · [[Type Inference]] · [[과잉 속성 체크 (Excess Property Checking)]] ## 🤖 LLM 활용 **언제**: untrusted boundary (API, form, env, LLM output) 매 parse. tool/function calling 의 매 input schema. **언제 X**: internal pure-TS code 매 over-validation 불필요. hot loop 의 매 매 parse 호출. ## ❌ 안티패턴 - **Anti1: parse everywhere**: 매 internal function 매 Zod parse — 매 overhead 누적, 매 type only 충분. - **Anti2: as cast after parse**: `Schema.parse(x) as MyType` — 매 redundant, parse 가 이미 typed return. - **Anti3: schema duplication**: type + schema 따로 정의 — 매 z.infer 사용. - **Anti4: nested transforms with side effects**: transform 안에서 fetch/IO — 매 pure 하게 유지. ## 🧪 검증 / 중복 - Verified (Zod docs colinhacks.com/zod, 2026 ecosystem). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — Zod runtime validation patterns + 2026 ecosystem context |