본문으로 건너뛰기

Zod Mini

Zod Mini는 Zod의 트리 셰이킹 가능한 변형입니다. 사용해 보려면 다음 패키지를 설치하세요.

npm install zod@^4.0.0

다음과 같이 가져옵니다.

import * as z from "zod/mini";

동일한 API는 독립 패키지인 @zod/mini로도 배포됩니다. 이 패키지는 zod/mini를 다시 내보내며, 이를 위해 zod를 피어 의존성으로 사용합니다. 버전도 zod를 따라갑니다.

npm install zod @zod/mini
import * as z from "@zod/mini";

Zod Mini는 zod와 완전히 동일한 기능을 구현하지만 함수형이며 트리 셰이킹 가능한 API를 사용합니다. zod에 익숙하다면 메서드 대신 함수를 주로 사용한다는 점이 가장 큰 차이입니다.

// regular Zod
const mySchema = z.string().optional().nullable();

// Zod Mini
const mySchema = z.nullable(z.optional(z.string()));

트리 셰이킹​

트리 셰이킹은 최신 번들러가 최종 번들에서 사용하지 않는 코드를 제거하는 기법입니다. 데드 코드 제거라고도 합니다.

일반 Zod의 스키마는 흔히 쓰는 작업을 위한 다양한 편의 메서드를 제공합니다(예: 문자열 스키마의 .min()). 번들러는 일반적으로 사용하지 않는 메서드 구현을 번들에서 제거("트리 셰이킹")하지 못하지만, 사용하지 않는 최상위 함수는 제거할 수 있습니다. 따라서 Zod Mini의 API는 메서드보다 함수를 더 많이 사용합니다.

// regular Zod
z.string().min(5).max(10).trim()

// Zod Mini
z.string().check(z.minLength(5), z.maxLength(10), z.trim());

번들 크기가 얼마나 줄어드는지 대략 파악하기 위해 다음과 같은 간단한 스크립트를 살펴보겠습니다.

z.boolean().parse(true)

이 스크립트를 각각 Zod와 Zod Mini로 번들링하면 크기는 다음과 같습니다. Zod Mini를 사용하면 64% 줄어듭니다.

패키지번들 크기(gzip)
Zod Mini2.12kb
Zod5.91kb

객체 타입을 포함하는 조금 더 복잡한 스키마에서는 다음과 같습니다.

const schema = z.object({ a: z.string(), b: z.number(), c: z.boolean() });

schema.parse({
a: "asdf",
b: 123,
c: true,
});
패키지번들 크기(gzip)
Zod Mini4.0kb
Zod13.1kb

이 수치로 대략적인 번들 크기를 파악할 수 있습니다. 수치를 꼼꼼히 살펴보고 직접 벤치마크한 뒤, 해당 사용 사례에서 Zod Mini를 선택할 가치가 있는지 판단하세요.

Zod Mini를 사용하는 경우(사용하지 않는 경우)​

일반적으로 번들 크기를 매우 엄격하게 제한해야 하는 경우가 아니라면 일반 Zod를 사용하는 편이 좋습니다. 많은 개발자가 번들 크기가 애플리케이션 성능에 미치는 영향을 지나치게 크게 평가합니다. 실제로 Zod 정도의 번들 크기(보통 5-10kb)는 농촌이나 개발도상 지역의 느린 모바일 네트워크 사용자를 위해 프런트엔드 번들을 최적화할 때에만 의미 있는 고려 사항입니다.

몇 가지 고려 사항을 살펴보겠습니다.

DX​

Zod Mini의 API는 더 장황하고 기능을 찾아보기 어렵습니다. Zod API의 메서드는 Zod Mini의 최상위 함수보다 IntelliSense에서 훨씬 쉽게 찾고 자동 완성할 수 있습니다. API를 연쇄 호출해 스키마를 빠르게 구성할 수도 없습니다. (Zod를 만든 사람으로서 Zod Mini API를 최대한 편리하게 설계하는 데 많은 시간을 들였지만, 여전히 표준 Zod API를 훨씬 선호합니다.)

백엔드 개발​

백엔드에서 Zod를 사용한다면 Zod 정도의 번들 크기는 큰 의미가 없습니다. Lambda처럼 리소스가 제한된 환경에서도 마찬가지입니다. 이 글은 여러 번들 크기에서 콜드 스타트 시간을 측정합니다. 다음은 그 결과 중 일부입니다.

번들 크기Lambda 콜드 스타트 시간
1kb171ms
17kb (일반 Zod의 gzip 압축 크기)171.6ms(보간값)
128kb176ms
256kb182ms
512kb279ms
1mb557ms

무시해도 될 만큼 작은 1kb 번들의 최소 콜드 스타트 시간은 171ms입니다. 그다음으로 테스트한 크기는 128kb였으며, 여기서도 5ms만 늘어났습니다. 일반 Zod 전체의 gzip 압축 크기는 약 17kb이므로 시작 시간 증가는 약 0.6ms에 해당합니다.

인터넷 속도​

일반적으로 서버 왕복 시간(100-200ms)은 추가 10kb를 다운로드하는 데 걸리는 시간보다 훨씬 깁니다. 느린 3G 연결(1Mbps 미만)에서만 추가 10kb의 다운로드 시간이 더 중요해집니다. 농촌이나 개발도상 지역의 사용자를 특별히 고려하는 경우가 아니라면 다른 부분을 최적화하는 데 시간을 쓰는 편이 낫습니다.

ZodMiniType​

모든 Zod Mini 스키마는 z.ZodMiniType 기본 클래스를 확장하며, 이 클래스는 다시 z.core.$ZodType를 확장합니다. 후자는 zod/v4/core에서 제공됩니다. 이 클래스는 ZodType보다 구현하는 메서드가 훨씬 적지만, zod에서 특히 유용한 몇 가지 메서드는 그대로 제공합니다.

.parse​

가장 기본적인 메서드입니다. 모든 Zod Mini 스키마는 zod와 동일한 파싱 메서드를 구현합니다.

import * as z from "zod/mini"

const mySchema = z.string();

mySchema.parse('asdf')
await mySchema.parseAsync('asdf')
mySchema.safeParse('asdf')
await mySchema.safeParseAsync('asdf')

.check()​

일반 Zod의 스키마 하위 클래스에는 흔히 쓰는 검사를 수행하는 전용 메서드가 있습니다.

import * as z from "zod";

z.string()
.min(5)
.max(10)
.refine(val => val.includes("@"))
.trim()

Zod Mini는 이러한 메서드를 구현하지 않습니다. 대신 .check() 메서드로 검사를 스키마에 전달합니다.

import * as z from "zod/mini"

z.string().check(
z.minLength(5),
z.maxLength(10),
z.refine(val => val.includes("@")),
z.trim()
);

다음 검사를 사용할 수 있습니다. 일부 검사는 특정 타입의 스키마(예: 문자열 또는 숫자)에만 적용됩니다. 모든 API는 타입 안전하므로 TypeScript가 지원되지 않는 검사를 스키마에 추가하지 못하게 합니다.

z.lt(value);
z.lte(value); // alias: z.maximum()
z.gt(value);
z.gte(value); // alias: z.minimum()
z.positive();
z.negative();
z.nonpositive();
z.nonnegative();
z.multipleOf(value);
z.maxSize(value);
z.minSize(value);
z.size(value);
z.maxLength(value);
z.minLength(value);
z.length(value);
z.regex(regex);
z.lowercase();
z.uppercase();
z.includes(value);
z.startsWith(value);
z.endsWith(value);
z.property(key, schema);
z.mime(value);

// custom checks
z.refine()
z.check() // replaces .superRefine()

// mutations (these do not change the inferred types)
z.overwrite(value => newValue);
z.normalize();
z.trim();
z.toLowerCase();
z.toUpperCase();

// metadata (registers schema in z.globalRegistry)
z.meta({ title: "...", description: "..." });
z.describe("...");

.register()​

레지스트리에 스키마를 등록할 때 사용합니다.

const myReg = z.registry<{title: string}>();

z.string().register(myReg, { title: "My cool string schema" });

.brand()​

스키마에 브랜드를 지정할 때 사용합니다. 자세한 내용은 브랜드 타입 문서를 참조하세요.

import * as z from "zod/mini"

const USD = z.string().brand("USD");

.clone(def)​

제공된 def를 사용해 현재 스키마와 동일한 복사본을 반환합니다.

const mySchema = z.string()

mySchema.clone(mySchema._zod.def);

기본 로케일 없음​

일반 Zod는 영어(en) 로케일을 자동으로 불러오지만 Zod Mini는 그렇지 않습니다. 오류 메시지가 필요 없거나, 다른 언어로 제공되거나, 별도로 사용자 지정되는 경우 번들 크기를 줄일 수 있습니다.

따라서 기본적으로 모든 이슈의 message 속성에는 "Invalid input"만 들어 있습니다. 영어 로케일을 불러오려면 다음과 같이 설정하세요.

import * as z from "zod/mini"

z.config(z.locales.en());

지역화에 대한 자세한 내용은 로케일 문서를 참조하세요.