Zod 스키마
이 능력은 Zod와 관련된 변경 사항(스키마 작성, 파싱 배치, 페이로드 디코딩, 에러 표시)이 있을 때마다 사용합니다. 이 능력은 스키마 레이어를 담당합니다: 형태, 파싱 호출, 해당 형태가 생성하는 타입, 생성되는 에러, 그리고 와이어 형식과 도메인 모델 간에 데이터를 전달하는 코덱.
다음 4가지 인접 질문은 각각 다른 능력이 담당하므로 이 능력의 범위가 아닙니다:
- 입력이 전혀 신뢰할 수 없는지 여부와 따라서 반드시 검사해야 하는지 — 애플리케이션 보안 능력의 판단입니다. 이 스킬은 해당 판단이 이미 내려졌다고 가정하고 Zod가 이를 어떻게 수행하는지 설명합니다.
- 파싱이 위치할 훅의 존재 여부 — 라우트 핸들러, 서버 함수, 폼 액션 — 프레임워크 능력의 사실입니다.
- 가져온 페이로드를 다시 가져오거나, 캐시하거나, 무효화할 시점 — 서버 상태 능력의 관심사입니다. 이 스킬은 자신이 담당하는 쿼리 함수 내에만 파싱을 배치합니다.
- 구문 분석된 결과를 컴포넌트가 어떻게 수신, 렌더링, 테스트하는지 — 컴포넌트 개발 능력의 관심사입니다.
여기 있는 규칙이 위 능력 중 하나와 대응되는 경우, 이 스킬은 Zod 메커니즘을 명시하고 다른 능력을 소유자로 지명합니다.
버전 규율. 이 스킬은 Zod 4 전용으로 작성되었습니다. Zod 3은 지원 대상에서 제외됩니다: 마이그레이션 경로, zod/v3 가이드라인, 또는 Zod 3 형태의 규칙이 없습니다. Zod 3이 등장하는 유일한 곳은 감지 신호로, 구식 관용구 목록을 제공하여 낡은 코드를 인식하고 현재 형태로 대체할 수 있도록 합니다. 이는 Zod 4 코드베이스에 대한 Zod 4 규칙이지, Zod 3을 다루는 것이 아닙니다. 여기서 경계가 평소보다 더 중요한 이유는, 튜토리얼, 포럼 게시물, 모델 회상 전반에 걸쳐 Zod 3 답변이 여전히 Zod 4 답변보다 우세하고, 대부분의 Zod 3 관용구가 Zod 4 설치 환경에서도 여전히 컴파일되기 때문에 "실행된다"는 사실이 규칙이 최신이라는 증거가 될 수 없기 때문입니다. 또한 Zod 4는 메이저 버전 내에서도 변경됩니다: z.xor, z.invertCodec, .brand 방향 매개변수는 4.0 이후에 추가되었습니다. 모든 버전에 민감한 설명은 검증된 대상을 명시하며, 표면이 변경될 것으로 알려진 곳의 규칙은 조회(lookup) 방식 — 설치된 버전의 공식 문서 https://zod.dev/ 참조 — 이지 고정된 API 이름이 아닙니다. 이 스킬이나 다른 곳에서 Zod API에 대한 버전이 명시되지 않은 주장은 신뢰할 수 없는 것으로 간주하십시오.
검증 기준 [email protected], 2026-05-04 게시 — https://zod.dev/ — TypeScript 5.5+ 및 strict: true 전제 조건과 함께. 각 참조 파일에는 규칙이 검증된 업스트림 페이지와 함께 자체 검증 기준 줄이 있습니다.
범위 밖. 모든 형태의 Zod 3. 4.5.0-canary 라인, 이에 대해 파생된 규칙이 없음. 다른 검증 라이브러리(단, 경계가 공유되는 경우는 제외 — Standard Schema 상호 운용성은 다루지만 Valibot 또는 ArkType의 자체 API는 다루지 않음). Zod 스키마를 생성하는 코드 생성 도구는 수동으로 작성해야 하는 부분이 무엇인지 명시할 때 언급되지만, 해당 도구의 자체 구성은 해당 도구의 책임입니다.
이 문서에서 핵심 단어 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", 및 "OPTIONAL"은 RFC 2119에 설명된 대로 해석되어야 합니다.
검증 경계
validation-boundary.md에서 다음 내용을 확인하세요:
- 데이터가 진입하는 경계에 하나의 파싱을 배치하고, 내부에서 다시 파싱하지 않음
- 파싱 이전에는
unknown, 이후에는z.infer<typeof Schema>이며, 수동 인터페이스는 사용하지 않음 - 애플리케이션 보안, 프레임워크, 서버 상태, 또는 컴포넌트 능력에 속하는 질문들
- 스키마가 비용 대비 효과를 보지 못하는 경우 — 내부 데이터, 이미 파싱된 값, 항목별 루프
- Zod를 다른 검증기와 함께 사용하고, 한 곳에서 변환
버전 및 패키지
version-and-packages.md에서 다음 내용을 확인하세요:
- 페이지를 신뢰하기 전에
zod.dev(Zod 4)와v3.zod.dev(Zod 3) 구분하기 zod,zod/mini,zod/locales,zod/v4/*고정 링크 중에서 선택하기- 가정된 TypeScript 5.5+ 및
strict: true, 이것 없이는 추론된 선택성이 불건전함 z.xor,z.invertCodec,.safeExtend,z.property,.brand방향에 대한 마이너 버전 최소 요구 사항- Zod 4에서 여전히 컴파일되는 Zod 3 관용구 (노후화 신호)
스키마 모듈
schema-modules.md에서 다음 내용을 확인하세요:
const X/type X및zX명명 규칙, 그리고 저장소당 하나 선택하기- 다른 스키마가 구성하는 어휘로서의 공유
EntityId,Timestamp,NonEmptyString프리미티브 .brand<"Name">(),"out"/"in"/"inout"방향, 그리고 파싱을 강제하는 픽스처.shape,.extend,.safeExtend,.pick,.omit,.merge()(4.4에서 리파인먼트 시 throw)를 사용한 파생- 모든 스키마를 요청 또는 렌더링마다 재구축하지 않고 모듈 범위로 호이스팅
프리미티브 및 포맷
primitives-and-formats.md에서 다음 내용을 확인하세요:
z.string().x()메서드를 대체한 최상위z.email(),z.url(),z.uuid(),z.iso.datetime()- 관대한
z.guid()대신 엄격한z.uuid()선택, 가져온 URL에는z.url()대신z.httpUrl()선택 - 프로젝트 자체 포맷을 위한
z.stringFormat()및 템플릿 리터럴 타입을 위한z.templateLiteral() z.int()의 안전한 정수 범위,Infinity거부, 고정 너비z.int32()및z.float64()- 비권장
z.nativeEnum()대신z.enum(),.exclude()및.extract()와 함께 사용
객체 및 컬렉션
objects-and-collections.md에서 다음 내용을 확인하세요:
z.object()가 모델링되지 않은 키를 제거하며,z.strictObject(),z.looseObject(),.catchall()이 이를 변경함- getter를 통한 자기 참조, 순환 오류에 필요한 반환 타입 어노테이션
- 두 개의 필수 인수를 가진
z.record(), 열거형 키 전체,z.partialRecord() .nonempty()가 더 이상 튜플 타입을 생성하지 않는z.tuple(),z.file()및z.json()- 요소별 스키마 대신 배열 스키마를 한 번만 파싱
유니온
unions.md에서 다음 내용을 확인하세요:
- 단순
z.union()보다 범위 축소, 에러 품질, 파싱 속도 측면에서 우수한z.discriminatedUnion() - 모든 브랜치가 고정해야 하는 리터럴 판별자, Zod 4가 내부에서 허용하는 중첩 유니온 및 파이프
- 두 브랜치에 모두 일치하는 값이 결함인 경우를 위한
z.xor() - 병합 불가능한 결과에
ZodError대신 일반Error를 throw하는z.intersection() - 하나의 객체여야 했던 선택적 필드 형태의 유니온
선택성 및 기본값
optionality-and-defaults.md에서 다음 내용을 확인하세요:
- 생산자가 실제로 내보내는 것에 따라
.optional(),.nullable(),.nullish()선택 - 출력 타입에서 단락(short-circuit)되는
.default()와 입력 타입에서 파싱하는.prefault() - 파싱 실패를 대체값으로 전환하는
.catch(), 그리고 이것이 언제 표류하는 생산자를 숨기는지 - 혼합된 null 및 undefined 생산자를 축소하기 위한
.nullish().transform((v) => v ?? null) exactOptionalPropertyTypes가 선택적 속성의 추론된 타입에 미치는 영향
리파인먼트
refinements.md에서 다음 내용을 확인하세요:
.refine(),.superRefine(),.check(), 그리고 이들 중 어느 것도 throw 해서는 안 된다는 규칙ctx.addIssue(),z.NEVER, 특정 필드에 오류를 전달하는path- 체인을 중단하는
abort, 이미 유효하지 않은 입력에 대한 검사를 건너뛰는 4.4의when - 구성된 스키마의 모든 기존 호출자에게
.parseAsync()를 강제하는 비동기 리파인먼트 - 스키마가 아닌 도메인에 속하는 권한, 할당량, 또는 고유성 규칙
트랜스폼 및 파이프
transforms-and-pipes.md에서 다음 내용을 확인하세요:
- 검증 후
.transform(), 검증 전z.preprocess(), 두 스키마 사이의.pipe() - 값은 변경되지만 타입과 그 내부 검사 가능성은 변경되지 않아야 하는
.overwrite() z.input대z.output/z.infer, 그리고 이들을 혼동하여 발생하는 형태 및 픽스처 버그ctx.issues.push또는z.NEVER와 함께 4.4의ctx.addIssue를 사용하여 트랜스폼 내부에서 실패 처리- 생산자 데이터를 재구성하는 것이 끝나고 도메인 결정이 시작되는 지점
코덱
codecs.md에서 다음 내용을 확인하세요:
.transform()이 역전할 수 없는 경계를 위한z.codec(input, output, { decode, encode })safe*및*Async형태의.decode()와.encode(),z.invertCodec()- 문서화된
isoDatetimeToDate,json,stringToURL,base64ToBytes템플릿 — 복사하여 사용, 가져오지 않음 - 디코드에만 적용되는 기본값 및
.catch(), 인코드 시 비-ZodError를 throw하는.transform() - 실패할 수 없는 전체 코덱, 따라서 제공하지 않는 형태 보장
강제 변환 및 구성
coercion-and-config.md에서 다음 내용을 확인하세요:
z.coerce.number().parse("")가0을 반환하고,z.coerce.boolean().parse("false")가true를 반환함- 참(true) 집합에도 거짓(false) 집합에도 속하지 않는 값을 거부하는 구조
z.stringbool() - 쿼리 문자열, 폼 데이터, 환경 변수 — 세 가지 문자열 값 소스
- 시작 시 한 번만 구성을 파싱하고, 나중에
undefined를 노출하는 대신 종료 - 비밀 구성을 로그 및 클라이언트 번들에서 제외
파싱
parsing.md에서 다음 내용을 확인하세요:
- 실패가 결함인 경우
.parse(), 분기할 결과인 경우.safeParse() - 스키마의 어떤 검사가 비동기이면 선택 사항이 아닌 필수 사항인
.parseAsync() - 파싱이 반환하는 깊은 복사, 이로 인해 깨지는 참조 동일성
- Zod가 도메인 시그니처에 남지 않도록
ZodError를 도메인 오류로 재매핑 - 경계와 이슈 경로를 로깅하고, 실패한 입력은 절대 로깅하지 않음
에러
errors.md에서 다음 내용을 확인하세요:
code및path를 포함하는error.issues, 제거된.errors및.formErrors별칭- 소비자가 선택하는
z.treeifyError(),z.flattenError(),z.prettifyError() message,invalid_type_error,errorMap을 대체한 통합error매개변수- Zod 4에서 역전된 우선순위 — 스키마, 파싱별,
z.config({ customError }), 로케일 - 신뢰할 수 없는 입력을 추적기나 클라이언트로 전달하는 오류에 넣는
reportInput
메타데이터 및 JSON 스키마
metadata-and-json-schema.md에서 다음 내용을 확인하세요:
.meta(),z.registry(),z.globalRegistry, 선언 병합을 통한GlobalMeta확장- 스키마 인스턴스에 바인딩되는 메타데이터, 따라서
.optional()또는.extend()가 이를 고아로 만듦 z.toJSONSchema()의target,io,unrepresentable,cycles,reused,uri옵션- 기본 출력 뷰와 다른 문서를 생성하는
io: "input" - JSON 스키마 표현이 없는
date,map,set,transform,bigint타입
모델 구조화된 출력
model-structured-output.md에서 다음 내용을 확인하세요:
generateObject또는 도구 계약으로서의 스키마, 원시 형태가 필요할 때satisfies z.ZodRawShape- 모델에 문서가 아닌 명령으로 도달하는
.describe()텍스트 .optional()및.nullish()가 거부되는 공급자의 엄격 모드에서 살아남는.nullable()- 모델 출력이 공급하는 도메인 타입에서 벗어나지 않도록
.shape.field재사용 - 잘못된 출력을 패치하는 대신 제한된 재시도 하에
ZodError로부터 다시 프롬프트
폼
forms.md에서 다음 내용을 확인하세요:
- Zod 고유의 이슈 상세 정보를 위한 Zod 특정 리졸버, 또는 라이브러리 독립성을 위한 Standard Schema 리졸버
- 폼의 값 및 기본값을 위한
z.input, 제출 핸들러를 위한z.output - 오류가 올바른 입력에 도달하는 유일한 방법인 교차 필드 리파인먼트의
path - 빈 숫자 필드가 제출하는 빈 문자열, 체크되지 않은 체크박스가 보내는 누락된 키
- 클라이언트 측 스키마가 절대 대체하지 못하는 서버 측 재파싱
데이터 저장소 경계
data-store-boundaries.md에서 다음 내용을 확인하세요:
- 저장된 형태와 도메인 모델, 이 둘 사이에 트랜스폼이 있는 두 개의 스키마
- 읽기 및 쓰기 절반을 하나의 쌍으로 작성하여 이름이 변경된 필드가 둘 사이에서 표류하지 않도록 함
- 패치를 위한
.partial(), 저장소 센티넬을 전달하는 생성 입력을 위한.omit().extend() - 드라이버의 자체 타입을 위한
z.instanceof(), 해당 필드 중 하나를 제한하는z.property() - 쿼리 깊이, 초안 상태, 로케일에 따라 달라지는 페이로드 — 그리고 답이 아닌 모든 선택적 스키마
성능 및 설치 공간
performance-and-footprint.md에서 다음 내용을 확인하세요:
- 게시된 약 14배, 7배, 6.5배 파싱 성능 향상, 약 25,000에서 약 175로의 타입 인스턴스화 감소
zod/mini의 함수형 API 및 1.88 kB 코어, 그리고 서버가 이를 채택해서는 안 되는 이유- 핫 경로 밖으로 호이스팅된 스키마 구성, 항목별이 아닌 한 번에 파싱된 컬렉션
- 측정된 경로에서
.refine()보다.check(), 선형 유니온보다 판별 유니온 - 키 제거 패스를 건너뛰는
z.looseObject(), 그리고 이로 인해 희생되는 보호
보안 태세
security-posture.md에서 다음 내용을 확인하세요:
- 대량 할당 보호로서의
z.object()의 키 제거, 그리고z.looseObject()가 다시 허용하는 것 - 통과하는 파싱이 HTML 이스케이핑, SQL 매개변수화, 또는 안전한 가져오기 보장이 아님
- 공격자가 제어하는 작업을 제한하기 위해
.regex()또는 값비싼 리파인먼트 앞에 배치된.max() - 무제한 배열, 무제한 재귀 깊이, 영원히 루프되는 순환 입력
- 허용 목록 응답 스키마, 없으면 페이로드와 함께 개인 필드가 유출됨
스키마 테스트
testing-schemas.md에서 다음 내용을 확인하세요:
z.email()이"nope"를 거부하는지 단언하는 것이 Zod 자체를 테스트하는 것이지 스키마를 테스트하는 것이 아닌 이유z.input<typeof Schema>를 픽스처 타입으로 사용, 관찰된 페이로드로부터 구축
…
[번역을 위해 잘림]