--- id: wiki-2026-0508-schema title: Schema category: 10_Wiki/Topics status: verified canonical_id: self aliases: [Data Schema, Schema Definition] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [schema, validation, zod, json-schema, database] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: TypeScript framework: Zod 4 --- # Schema ## 매 한 줄 > **"매 schema 는 data 의 contract — 매 runtime + compile-time 양쪽에서 enforce"**. 매 origin 은 1970s relational DB DDL; 매 modern state 는 Zod 4 (TS-first, runtime + types), JSON Schema 2020-12, Schema.org (web), Avro/Protobuf (wire), Postgres 17 declarative migration. ## 매 핵심 ### 매 schema layer (매 stack 별) - **DB schema**: DDL (Postgres `CREATE TABLE`), constraints, indexes. - **Wire schema**: Protobuf, Avro, JSON Schema — 매 inter-service contract. - **App-runtime schema**: Zod, Pydantic, Valibot — 매 API boundary 의 validation. - **Web schema**: Schema.org JSON-LD — 매 SEO + semantic web. - **AI schema**: 매 LLM structured output (OpenAI structured outputs, Anthropic tool use schema). ### 매 Zod 4 의 modern 위치 (2026) - 매 single source of truth → infer TS type + runtime validate + JSON Schema 변환. - v4 (2026) 의 major: pure-ESM, smaller bundle, sync error throw, `.brand()` first-class. ### 매 응용 1. API request/response validation (Hono, tRPC, Next.js Route Handler). 2. Form validation (React Hook Form + Zod resolver). 3. LLM structured output (Anthropic tool schema, function calling). 4. Config validation (env vars at boot). ## 💻 패턴 ### 매 Zod 4 schema → TS type infer ```ts 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.date(), }); export type User = z.infer; // → { id: string; email: string; age: number; role: "admin"|"user"|"guest"; createdAt: Date } const parsed = UserSchema.parse(rawJson); // 매 throw on invalid const safe = UserSchema.safeParse(rawJson); // 매 { success, data | error } ``` ### 매 Zod → JSON Schema (LLM structured output) ```ts import { z } from "zod"; import { zodToJsonSchema } from "zod-to-json-schema"; const ExtractedInvoice = z.object({ vendor: z.string(), total: z.number(), lineItems: z.array(z.object({ desc: z.string(), qty: z.number().int(), unitPrice: z.number(), })), }); const jsonSchema = zodToJsonSchema(ExtractedInvoice); // 매 Anthropic tool input_schema 에 그대로 사용 ``` ### 매 Anthropic tool use (Claude Opus 4.7 structured output) ```python import anthropic client = anthropic.Anthropic() resp = client.messages.create( model="claude-opus-4-7", max_tokens=2048, tools=[{ "name": "extract_invoice", "description": "Extract structured invoice data", "input_schema": { "type": "object", "properties": { "vendor": {"type": "string"}, "total": {"type": "number"}, "line_items": { "type": "array", "items": { "type": "object", "properties": { "desc": {"type": "string"}, "qty": {"type": "integer"}, "unit_price": {"type": "number"}, }, "required": ["desc", "qty", "unit_price"], }, }, }, "required": ["vendor", "total", "line_items"], }, }], tool_choice={"type": "tool", "name": "extract_invoice"}, messages=[{"role": "user", "content": invoice_text}], ) # resp.content[0].input → 매 schema-validated dict ``` ### 매 Postgres 17 declarative schema (with constraints) ```sql CREATE TABLE users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), email TEXT NOT NULL UNIQUE CHECK (email ~ '^[^@]+@[^@]+$'), age INT NOT NULL CHECK (age >= 0 AND age <= 150), role TEXT NOT NULL CHECK (role IN ('admin','user','guest')), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX idx_users_email_lower ON users (LOWER(email)); CREATE INDEX idx_users_role_created ON users (role, created_at DESC); ``` ### 매 Schema.org JSON-LD (SEO, 매 Article) ```html ``` ### 매 Pydantic v2 (Python, 매 FastAPI 와 함께) ```python from pydantic import BaseModel, EmailStr, Field from typing import Literal class User(BaseModel): id: str email: EmailStr age: int = Field(ge=0, le=150) role: Literal["admin", "user", "guest"] # FastAPI route — 매 자동 OpenAPI + validation @app.post("/users") def create_user(user: User) -> User: return user ``` ### 매 schema migration (Drizzle, TS 2026) ```ts // schema.ts — 매 source of truth import { pgTable, uuid, text, integer, timestamp } from "drizzle-orm/pg-core"; export const users = pgTable("users", { id: uuid("id").primaryKey().defaultRandom(), email: text("email").notNull().unique(), age: integer("age").notNull(), role: text("role", { enum: ["admin","user","guest"] }).notNull(), createdAt: timestamp("created_at").notNull().defaultNow(), }); // $ drizzle-kit generate → 매 SQL migration auto // $ drizzle-kit migrate ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | 매 TS API boundary | Zod 4 | | 매 Python API | Pydantic v2 | | 매 cross-language wire | Protobuf / Avro | | 매 LLM structured output | JSON Schema (via Zod/Pydantic) | | 매 SEO web page | Schema.org JSON-LD | | 매 RDB | Postgres DDL + Drizzle/Prisma migration | **기본값**: TS 면 Zod 4 (single source → type + runtime + JSON Schema). ## 🔗 Graph - 부모: [[TypeScript 타입 시스템 (TypeScript Type System)|Type System]] · [[Validation]] - 변형: [[Zod]] · [[JSON Schema]] · [[Protobuf]] - 응용: [[API Design]] - Adjacent: [[OpenAPI]] ## 🤖 LLM 활용 **언제**: 매 LLM structured output 강제 (tool use input_schema). 매 unstructured text → typed object extraction. **언제 X**: 매 schema 자체의 design — 매 domain modeling 은 human 의 일. ## ❌ 안티패턴 - **`z.any()` everywhere**: 매 schema 의 의미 X. - **Schema drift**: API schema 와 DB schema 가 따로 → 매 single source 필요. - **Over-validation in hot path**: 매 매 request 마다 deep validate → 매 boundary 만 validate. - **Stringly-typed enums**: 매 `z.string()` for role → 매 `z.enum()` 으로 narrow. ## 🧪 검증 / 중복 - Verified (Zod docs v4, JSON Schema 2020-12 spec, Schema.org, Postgres 17 docs). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — Zod 4 + Pydantic v2 + LLM tool schema + Drizzle |