글 원문: https://blog.logrocket.com/comparing-schema-validation-libraries-zod-vs-yup/
편집자 주: 이 글은 사용 편의성, 라이브러리 생태계, 통합(integration)을 기반으로 Zod와 Yup을 비교하기 위해 2023년 9월 5일에 마지막으로 업데이트되었습니다.
웹 개발자는 애플리케이션에서 수많은 복잡한 데이터 교환을 처리해야 합니다. 잘못된 데이터 유형으로 인해 에러가 발생할 수 있으므로, 앱에서 보내거나 받는 모든 데이터의 유효성을 검사하는 것이 중요합니다. 예상치 못한 데이터 유형이나 값은 이를 처리하는 애플리케이션에 충돌을 일으키고 다른 바람직하지 않은 결과를 초래할 수도 있습니다.
스키마 유효성 검사는 스키마를 생성하여 데이터를 검증하는 프로세스입니다. 스키마의 정의는 구현에 따라 다를 수 있지만 JavaScript에서 스키마는 일반적으로 해당 데이터의 유효성을 검사하는 데 사용되는 객체 데이터 유형입니다.
이 가이드에서는 널리 사용되는 두 가지 스키마 유효성 검사 라이브러리인 Zod와 Yup을 비교하여 기본 요소, 객체 및 배열과 같은 일반적인 데이터 패턴을 유효성 검사할 때 어떤 성능을 보이는지 살펴보겠습니다. 또한 성능 메트릭, 러닝 커브, 라이브러리 생태계 및 통합도 비교하겠습니다.
아래는 자바스크립트 스키마의 간단한 예시입니다:
const userDataSchema = {
name: "string",
age: "number",
};
const userData = {
name: "Peter",
age: "15",
};
function validateSchema(schema, data) {
for (let property in schema) {
if (data[property] === undefined) {
console.error(property + " is required.");
} else if (typeof data[property] !== schema[property]) {
console.error(
property +
" must be of type " +
schema[property] +
", " +
typeof data[property] +
" found."
);
} else {
console.log(property + " is validated!");
}
}
}
validateSchema(userDataSchema, userData);
위의 코드에서 userDataSchema 객체는 스키마 역할로, userData 객체에 name 및 age 속성이 있는지와 값의 유형을 검증합니다. 위 코드에서 이름은 문자열, 나이는 숫자여야 합니다.
물론 유효성 검사 스키마 함수만으로는 복잡한 애플리케이션에 사용하기에 충분하지 않습니다. 복잡한 데이터 구조의 유효성을 검사하고, 원치 않는 동작을 일으키지 않도록 하려면 더 강력한 기능이 필요합니다. 바로 이때 스키마 유효성 검사 라이브러리가 필요합니다.
다행히도 전 세계의 관대한 JavaScript 개발자들이 npm에서 무수히 많은 오픈 소스 스키마 유효성 검사 라이브러리를 공유해 왔습니다. Zod가 등장하여 Yup의 자리를 빼앗기 전까지 Yup은 가장 인기 있는 JavaScript 스키마 유효성 검사 라이브러리 중 하나였습니다.
Zod는 의존성이 없는 TypeScript 우선 데이터 유효성 검사 라이브러리로, TypeScript와 JavaScript 애플리케이션 모두에서 데이터를 정의하고 유효성을 검사할 수 있는 간단하고 직관적인 방법을 제공합니다. Zod는 간단한 구문으로 독립적이고 경량화되도록 설계되어 다양한 TypeScript 및 JavaScript 환경에서 쉽게 사용할 수 있습니다.
조드의 몇 가지 주목할 만한 기능은 다음과 같습니다:
이 섹션에서는 사용하게 될 몇 가지 일반적인 데이터 유효성 검사 API를 살펴보겠습니다.
Zod의 원시 타입에는 string, number, BigInt, Boolean, date, symbol 등이 포함됩니다. 또한 Zod는 빈 값에 대한 스키마를 지정하는 API와 any 및 unknown과 같은 모든 유형을 잡기 위한 스키마도 제공합니다:
const zod = require('zod');
// or import { zod } from 'zod'
const schema = zod.string()
schema.parse('hello');
위의 예는 간단한 문자열 스키마 유효성 검사를 보여줍니다. Zod는 유효성 검사를 위한 광범위한 메서드를 제공합니다:
const passwordSchema = zod.string().min(8).max(16);
passwordSchema.parse("3same33");
숫자를 검증하기 위해 다른 확장된 방법을 사용할 수도 있습니다.
const ageSchema = zod.number().min(18);
ageSchema.parse(12); // throws error: Number must be greater than or equal to 18
위와 같은 에러 메시지는 최상의 경험을 제공하지는 않으므로, 커스텀 에러 메시지를 사용할 수 있습니다:
const ageSchema = zod
.number()
.min(18, { message: "too young to play" })
.max(40, { message: "too old to play" });
ageSchema.parse(12) // throws error: too young to play
유효성 검사 방법 외에도 Zod는 문자열 값의 형식을 지정하는 데 도움이 되는 변환 방법도 제공합니다.
const usernameSchema = zod.string().toLowerCase();
const emailSchema = zod.string().trim().email();
console.log(usernameSchema.parse("John_Doe")); // logs john_doe
emailSchema.parse(" doe@mail.com ");
위 코드에서 usernameSchema
는 파싱된 문자열의 반환 값을 소문자 문자열로 변환합니다. emailSchema
는 유효한 이메일인지 아닌지 확인하기 전에 trim
메서드를 사용하여 문자열 주변의 모든 공백을 잘라냅니다.
이상적으로, 위의 예에서 "user"라는 스키마를 생성하려면 객체가 필요합니다. 그럼 위의 모든 예를 객체 스키마에 통합하는 방법을 살펴보겠습니다.
const userSchema = zod.object({
username: zod.string().toLowerCase(),
email: zod.string().trim().email(),
age: zod.number().positive(),
password: zod.string().min(8).max(16),
});
userSchema.parse({
username: "john_doe",
email: "doe@gmail.com",
age: -24,
password: "not-my-favorite",
});
이 경우 Zod는 발생하는 순서대로 모든 유효성 검사 에러를 발생시킵니다. 따라서 username
과 age
가 모두 유효하지 않으면, 두 스키마 모두에 대한 에러 정보가 포함된 배열을 얻게 됩니다.
기본적으로 객체의 모든 속성은 필수입니다. 그러나 선택적인 속성을 갖고 싶다면 optional
메서드를 사용하면 됩니다:
const userSchema = zod.object({
username: zod.string().toLowerCase(),
email: zod.string().trim().email(),
age: zod.number().positive(),
password: zod.string().min(8).max(16),
familySecret: zod.string().optional()
});
Zod는 배열 값의 유효성을 검사하는 array
메서드를 제공합니다. 예를 들어, .min
및 .max
함수를 사용하여 배열의 최소 또는 최대 길이를 확인할 수 있습니다:
const FruitSchema = zod.array(zod.string()).min(3).max(5);
fruitsSchema.parse(["orange", "banana", "apple"]);
이미 생성된 스키마를 array
메서드에 전달할 수도 있습니다. 이전 섹션의 userSchema
예제를 다시 사용해 보겠습니다.
const usersSchema = zod.array(userSchema);
const users = [
{
username: "john_doe",
email: "doe@gmail.com",
age: -24,
password: "not-my-favorite",
},
];
usersSchema.parse(users); // TypeError: Number must be greater than 0
tuple
메서드는 고정된 수의 요소 및 다양한 데이터 유형으로 배열을 생성하는 또 다른 특별한 Zod API입니다.
const athleteSchema = zod.tuple([
// takes an array of schemas
zod.string(), // a string for name
zod.number(), // a number for jersey
zod.object({
pointsScored: zod.number(),
}), // an object with property pointsScored that has number value
]);
athleteSchema.parse(["James", 23, { pointsScored: 7 }])
튜플로 파싱된 데이터는 스키마 구조와 정확히 일치하는 배열이어야 합니다.
Zod는 함수의 유효성을 검사하고, 함수에 전달된 데이터와 함수에서 반환된 데이터가 올바른지 확인할 수 있습니다. 함수 스키마는 두 개의 API(args
및 returns
)를 사용하여 함수의 입력과 출력을 검증합니다.
const sumOfNumsSchema = zod
.function()
.args(zod.number(), zod.number())
.returns(zod.number());
const sumOfNums = sumOfNumsSchema.validate((val1, val2) => {
return val1 + val2;
});
sumOfNums("1", 33); // TypeError: Expected number, received string
지금까지 본 다른 유효성 검사와 달리, Zod의 함수 유효성 검사에서는 .parse
를 사용하지 않습니다. 함수 유효성 검사는 Zod에만 있습니다; Yup에는 상응하는 API가 없습니다.
Zod에는 optional한 스키마를 정의하는 몇 가지 유니크한 API도 있습니다. 예를 들어, union 메소드를 사용하여 "OR" 타입을 구성할 수 있습니다. 예를 들어, 데이터가 문자열 "또는" 숫자인 스키마를 생성하려면 다음을 수행하십시오.
const productId = zod.union([zod.string(), zod.number()]);
productId.parse(222);
productId.parse('I9290JEI');
productId.parse(false); // TypeError: Invalid input
Zod의 유니크한 API 중 또 다른 하나는 intersection 메서드로, 두 스키마를 결합하여 논리적 "AND" 유형을 생성하는 데 특히 유용합니다. 예를 들어:
const userId = zod.object({
id: zod.number(),
});
const baseTeacher = zod.object({
name: zod.string(),
});
const teacher = zod.intersection(baseTeacher, userId);
teacher.parse({ name: "Mr Doe" }); // TypeError: id is required
Yup은 form 및 data processing에서 데이터 스키마의 유효성을 검사하는 데 가장 널리 사용되는 JavaScript 유효성 검사 라이브러리 중 하나입니다. 핵심 기능 중 일부는 다음과 같습니다.
Yup을 사용한 스키마 유효성 검사는 Zod와 유사하지만 구문이 다릅니다. 일반적인 사용 및 확장성 측면에서 두 라이브러리는 동일한 작업을 다른 방식으로 수행합니다.
예를 들어, 다음은 Yup에서 문자열의 유효성을 검사하는 방법입니다:
const yup = require('yup')
// or import * as yup from yup
const schema = yup.string();
schema.validate(333).then((res) => console.log(res)); // returns 333
schema.isValid(11).then((res) => console.log(res)); // returns true
validate
메서드는 Zod의 parse
메서드와 유사합니다. 두 함수 모두 객체의 유효성을 검사하는 대신 실제로 객체를 파싱합니다. 이는 두 함수 모두 주어진 데이터를 가져와서 다시 반환하려고 시도한다는 의미입니다. 파서에 에러가 발생하면 런타임이 종료되고 에러를 던집니다.
한편, isValid
메서드는 데이터의 유효성만 검사하고 에러 핸들링은 사용자에게 맡깁니다. 따라서 Yup의 validate
메서드와 Zod의 parse
메서드를 사용할 때 에러 캐치를 고려해야 합니다.
위의 예에서 숫자가 값으로 전달될 때 string
으로 지정한 스키마가 에러를 발생시키지 않는 이유가 궁금할 것입니다. 이는 기본적으로 엄격(not strict by default)하지 않기 때문입니다. 숫자를 쉽게 문자열로 변환할 수 있으므로 오류가 발생하지 않습니다. 그러나 strict
API를 사용하면 이 기본 동작을 재정의할 수 있습니다.
const schema = yup.string().strict();
schema.validate(333).then((res) => console.log(res)); // TypeError: this must be a `string` type, but the final value was: `333`
Zod와 마찬가지로 Yup도 검증 및 변환을 위한 광범위한 API를 제공합니다.
const userSchema = yup
.object({
username: yup.string().lowercase().required(),
email: yup.string().email().required(),
age: yup.number().positive().required(),
password: yup.string().min(8).max(16).required(),
familySecret: yup.string(),
})
.strict();
userSchema
.validate({
username: "John_Doe",
email: "doe@gmail.com",
password: "not-my-favorite",
})
.then((res) => console.log(res)); // TypeError: age is a required field
배열 타입의 경우 Yup에는 해당 값의 유효성을 검사하는 몇 가지 유용한 확장 기능이 있습니다. 예를 들어, .min
및 .max
함수를 사용하여 배열의 최소 또는 최대 길이를 확인할 수 있습니다. .of
메서드를 사용하여 해당 값의 유형을 확인할 수도 있습니다.
// validate that the data is an array with number as its value.
// The minimum value of the array is two (배열 원소의 최솟값은 2)
// The minimum length of the array is four (배열 길이의 최솟값은 4)
const schema = yup.array().of(yup.number().min(2)).min(4);
schema.validate([2]).then((res) => {
console.log(res); // typerror: this field must have at least 4 items
});
Zod와 Yup은 모두 TypeScript를 지원합니다. Zod는 TypeScript 최고 수준의 지원을 제공합니다. 이러한 라이브러리를 사용하면, 데이터 유효성을 검사하는 데 사용할 수 있는 TypeScript type 별칭을 추론할 수 있습니다.
간단히 말해서, Yup 또는 Zod의 스키마에서 type
별칭을 생성하여 변수가 올바른 데이터 유형인지 확인할 수 있습니다.
import * as yup from "yup";
import * as zod from "zod";
const yupSchema = yup.string()
type A = yup.InferType<typeof yupSchema>
const x: A = 12 // wrong, but nothing happens
const zodSchema = zod.string();
type B = zod.infer<typeof zodSchema>; // string
const y: B = 12; // TypeError
TypeScript를 사용하여 위의 스크립트를 실행할 수 있습니다. x
값이 숫자가 아닌 문자열이어야 함에도 불구하고 Yup은 아무 작업도 수행하지 않고, Zod는 실제로 에러를 발생시킵니다.
두 라이브러리의 성능을 비교하는 것은 특정 사용 사례, 검증 스키마의 복잡성, 검증되는 데이터의 크기에 따라 크게 달라집니다.
간단한 유효성 검사 규칙과 소규모 데이터 세트의 경우 Zod와 Yup의 성능 차이가 눈에 띄지 않을 수 있습니다. 그러나 매우 복잡한 스키마나 대규모 데이터 세트의 경우 성능 특성이 다를 수 있습니다. 따라서 애플리케이션에 중요한 특정 검증 시나리오를 벤치마킹하는 것을 고려해 보십시오.
Yup과 Zod는 모두 배우기가 매우 간단하고 사용하기 쉽습니다. 이들 문법은 매우 유사하여 전체 코드베이스를 한 코드에서 다른 코드로 쉽게 전환할 수 있습니다.
Zod는 TypeScript를 기본 고려 사항으로 설계되었습니다. 즉, TypeScript를 지원하도록 구축된 Yup과 달리 TypeScript 프로젝트와 원활하게 통합됩니다.
따라서 이미 TypeScript 프로젝트가 있다면 Zod 사용을 고려해 보세요. Zod는 종속성이 전혀 없는 것으로도 알려져 있어 추가 종속성을 걱정할 필요 없이 가볍고 쉽게 프로젝트에 통합할 수 있습니다.
Zod와 Yup 모두 포괄적인 문서를 제공하므로 학습이 더 쉽고 빠릅니다. 두 라이브러리 모두 대규모 커뮤니티 채택을 통해 강력한 커뮤니티 지원을 보장하므로 오랫동안 버그에 갇히지 않습니다.
Yup은 Formik과 같은 인기 있는 form 라이브러리와의 쉬운 통합으로 매우 유명하지만, Zod는 React Hook Form과 같은 다른 form 라이브러리와도 잘 통합됩니다. Zod는 가끔 다른 form 라이브러리와 원활하게 통합하기 위해 추가적인 서드 파티 라이브러리가 필요할 수도 있습니다.
Zod와 Yup은 스키마 유효성 검사를 위한 유일한 JavaScript 및 TypeScript 라이브러리가 아닙니다. 사실 joi나 AJV 같은 라이브러리만큼 인기가 있지는 않습니다.
joi는 주로 데이터 유효성 검사 규칙을 정의하고 적용하는 데 사용되는 인기 있는 JavaScript 유효성 검사 라이브러리입니다. 서버 사이드 및 클라이언트 사이드 애플리케이션 모두에서 널리 사용됩니다. 여기에는 Yup 및 Zod에서 본 것과 유사한 자체 스키마 정의 구문이 있습니다.
AJV는 가장 널리 사용되는 스키마 검증 라이브러리라고 해도 과언이 아닙니다. 스키마 기반 검증에 중점을 둔 joi와 달리 AJV는 JSON 스키마 검증을 위해 특별히 설계되었습니다. [JSON 스키마 표준](JSON Schema standard)을 준수하며 JSON 스키마에 대해 JSON 데이터의 효율적인 유효성 검사를 제공합니다. AJV는 API 및 데이터 처리 파이프라인에서 자주 사용됩니다.
joi와 AJV는 복잡한 검증 시나리오에 더 중점을 두고 있으며 크고 복잡한 검증 규칙을 처리하는 데 적합합니다. Yup과 Zod는 더 간단한 사용 사례와 양식 유효성 검사를 위해 선호되는 경우가 많습니다.
위의 비교에서 볼 수 있듯이 Zod와 Yup에는 모두 스키마를 사용하여 데이터의 유효성을 검사하는 간단한 API가 있습니다. 예, 숫자 스키마의 자르기 및 반올림 방법과 같이 특정 상황에서 유용할 수 있는 데이터 유효성 검사 이외의 일부 기능이 있습니다.
Zod는 함수의 입력과 출력을 검증하여 모든 데이터가 올바른지 확인할 수 있습니다. 또한 오류가 발생할 경우 런타임을 종료하는 훌륭한 TypeScript 지원 기능이 있지만 Yup은 유추된 유형이 잘못된 경우 아무 작업도 수행하지 않습니다. 게다가 Zod에는 union 및 intersection와 같은 선택적 스키마를 정의하는 몇 가지 고유한 기능이 있습니다.
그렇다면 여러분의 다음 프로젝트에는 어떤 스키마 검증 라이브러리를 사용해야 할까요? 이는 귀하의 애플리케이션 요구 사항에 따라 다릅니다. Yup의 광범위한 기능은 form에 사용되는 많은 패턴, 심지어 반올림을 수행해야 하는 상황에 따른 패턴까지 포함하므로 form 유효성 검사를 많이 수행하는 경우 Yup을 사용하는 것이 좋습니다.
그러나 API 데이터 교환이 많고 클라이언트와 서버 간에 전달되는 모든 데이터의 유효성을 검사해야 하는 경우 Zod가 최선의 선택일 수 있습니다. 특히 TypeScript를 사용하는 경우 더욱 그렇습니다.