본문으로 건너뛰기

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());

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