[ 살펴보기 ] Zod - Object, Array and others
![[ 살펴보기 ] Zod - Object, Array and others](https://cdn.hashnode.com/res/hashnode/image/upload/v1724917767026/c45bdfac-b703-493b-a01c-7500075f4ddc.jpeg)
Zod를 통해 primitive type 검사 뿐만 아니라 object나 array에 대한 validation 역시 할 수 있다.
Object
다음은 object를 validation하는 간단한 예제다.
import { z } from "zod";
const testSchema = z.object({
name: z.string(),
age: z.number(),
});
const testObj = {
name: "test",
age: 20,
};
const test = testSchema.parse(testObj);
/*
object property와 data type이 zod object schame와
일치 하므로 pass
*/
const testInvalidObj = {
name: "test",
age: "20",
};
const test = testSchema.parse(testInvalidObj);
// age property가 string이므로 error
만일 생성한 zod object schema를 기반으로 타입을 생성하고 싶다면 다음과 같이 할 수 있다.
type TestSchema = z.infer<typeof testSchema>;
생성한 object schema의 특정 property에 접근하고자 한다면 생성한 object schema의 shape property를 통해 접근할 수 있다.
const testSchema = z.object({
name: z.string(),
age: z.number(),
});
const ageTestSchema = testSchema.shape.age;
ageTestSchema.parse(20);
만약 생성한 object schema의 propery key를 기반으로 Zod enum을 생성하고자 한다면 object schema의 keyof method를 사용할 수 있다.
const testSchema = z.object({
name: z.string(),
age: z.number(),
});
const testSchemaKeys = testSchema.keyof();
// testSchemaKeys는 다음과 동일하다 z.enum(["name", "age"])
기존에 생성한 object schema에 다른 propery를 추가하여 확장하고 싶다면 extend method를 통해 확장할 수 있다.
const testSchema = z.object({
name: z.string(),
age: z.number(),
});
const extendedTestSchema = testSchema.extend({
nation: z.string(),
});
const testInvalidObj = {
name: "test",
age: 20,
};
const test = extendedTestSchema.parse(testInvalidObj);
// nation property가 존재하지 않으므로 error
비슷한 method로 두 개의 schema를 합쳐 새로운 schema를 만드는 merge method가 있다.
const testSchema = z.object({
name: z.string(),
age: z.number(),
});
const newTestSchema = z.object({
address: z.string(),
});
const mergedTestSchema = testSchema.merge(newTestSchema);
Typescirpt의 utility처럼 zod에서도 pick, omit, partial, required 등의 method를 제공한다. 우선 object schema의 특정한 property만 선택하거나 제외할 수 있는 pick과 omit method를 살펴보자.
const testSchema = z.object({
name: z.string(),
age: z.number(),
address: z.string(),
});
const pickedTestSchema = testSchema.pick({ age: true });
const omittedTestSchema = testSchema.omit({ age: true });
const testPickedObj = {
age: 20,
};
const testOmittedObj = {
name: "test",
address: "test addres",
};
const testPick = pickedTestSchema.parse(testPickedObj);
// validation pass
const testOmit = omittedTestSchema.parse(testOmittedObj);
// validation pass
Partial이나 deepPartial을 통해 object schema property 모두 optional로 전환할 수도 있다.
const testSchema = z.object({
name: z.string(),
age: z.number(),
address: z.string(),
});
const testPartialSchema = testSchema.partial();
const testObj = {
name: "test",
address: "test addres",
};
const test = testPartialSchema.parse(testObj);
/*
partial schema를 통해 검사를 하므로 testObj에
age property가 없어도 validation pass
*/
schema의 모든 property를 optional로 만드는 것이 아닌 특정 property만 optional로 만들고 싶다면 partial method에 타겟 property를 인자로 넘긴다.
const testPartialSchema = testSchema.partial({ address: true });
주의할 점은 일반 partial method는 shallow level로 partial을 적용하므로 nested obejct로 구성된 object schema는 deepPartial을 통해 partial을 적용한다.
const testSchema = z.object({
name: z.string(),
age: z.number(),
location: z.object({
latitude: z.number(),
longitude: z.number(),
})
});
const testPartialSchema = testSchema.deepPartial();
반면 required method는 object shema의 모든 property를 필수로 만든다.
const testSchema = z.object({
name: z.string(),
age: z.number(),
address: z.string().optional(),
});
const testRequiredSchema = testSchema.required();
const testObj = {
name: "test",
age: 20,
};
const test = testRequiredSchema.parse(testObj);
/*
검사시 address property도 optional이 아닌 필수로 취급하므로
validation fail
*/
schema의 모든 property를 required로 만드는 것이 아닌 특정 property만 required로 만들고 싶다면 required method에 타겟property를 인자로 넘긴다.
const testPartialSchema = testSchema.required({ address: true });
Zod는 parse를 통해 validation에 성공하면 parse method에 전달 했던 인자를 그대로 return한다. 만약 parse method에 object schema에 존재하지 않는 property를 함께 넣으면 validation이 성공했을 때 return되는 값 중에 schema에 존재하지 않는 property는 제외된다.
const testSchema = z.object({
name: z.string(),
age: z.number(),
address: z.string().optional(),
});
const testObj = {
name: "test",
age: 20,
address: "test address",
nationality: "test nationality",
};
const test = testSchema.parse(testObj);
/*
{
"name": "test",
"age": 20,
"address": "test address"
}
*/
만약에 validation 성공 이후 return되는 값에 shema에 존재하지 않는 값도 포함시고 싶다면 parse method 이전에 passthrough method를 추가해 준다.
...
const test = testSchema.passthrough().parse(testObj);
/*
{
"name": "test",
"age": 20,
"address": "test address",
"nationality": "test nationality" // nationality도 포함된다.
}
*/
위에서 살펴보았듯이 zod는 default로 object shema에 존재하지 않는 property를 가진 object를 parse에 전달하면 return 결과에서 해당 property를 제외해버린다.
만약 object schema에 존재하지 않는 property를 전달하지 못하게 막고 싶다면 strict method를 parse method 전에 추가해준다. strict가 적용되면 shema에 존재하지 않는 property가 전달되면 오류가 발생한다.
const testSchema = z.object({
name: z.string(),
age: z.number(),
address: z.string().optional(),
});
const testObj = {
name: "test",
age: 20,
address: "test address",
nationality: "test nationality",
};
const test = testSchema.strict().parse(testObj);
/*
strict로 인해 schema 존재하지 않는 proprty가 전달되면
오류 발생
*/
만약 schema에 존재하지 않는 property를 허용하되 해당 property의 데이터 타입을 검사해야 하는 경우라면 어떻게 해야할까? 그럴 때는 catchall method를 활용할 수 있다.
const testSchema = z
.object({
name: z.string(),
age: z.number(),
address: z.string().optional(),
})
.catchall(z.string());
const testObj = {
name: "test",
age: 20,
address: "test address",
nationality: "test nationality",
};
const testInvalidObj = {
name: "test",
age: 20,
address: "test address",
phone:123
};
const test = testSchema.parse(testObj);
/*
catcheall의 요구 type이 string이고 testObj가
가지고 있는 추가 property인 nationality 역시 string이므로
validation pass
*/
const testInvalid = testSchema.parse(testInvalidObj);
/*
반명 testInvalidObj가 가지고 있는 추가 property인
phone은 number type이고 catchall의 조건에 부합하지 못하므로
validation fail
*/
참고로 catchall을 적용한 schema로 validation 검사를 하면 parse 이전에 strict가 적용되더라도 무시된다. 또한 validation이 성공했을 때 schema에 존재하지 않는 property도 return 값에 함께 포함된다.
...
const test = testSchema.parse(testObj);
/*
{
"name": "test",
"age": 20,
"address": "test address",
"nationality": "test nationality"
}
*/
Array
다음은 array data인지 validation하는 간단한 예제다.
const testSchema = z.array(z.string());
const testArr = ["test", "test2"];
const test = testSchema.parse(testArr);
Array schema를 아래와 같이 구성할 수도 있다.
const testSchema = z.string().array();
// z.array(z.string())와 동일하다.
다만 위와 같이 구성할 때 optional과 같은 추가 method를 연결한다면 적용하는 순서에 유의해야 한다.
z.string().optional().array(); // (string | undefined)[]
z.string().array().optional(); // string[] | undefined
Array validation시 empty array를 허용하지 않고 싶으면 nonempty method를 사용한다.
const testSchema = z.array(z.string()).nonempty();
// 또는 const testSchema = z.string().array().nonempty();
const testArr = [];
const test = testSchema.parse(testArr);
// empty array이므로 validation fail
Array의 length를 제한하고 싶다면 length, min, max method를 활용할 수 있다.
z.string().array().min(5);
// array length가 5 이상일 때 pass
z.string().array().max(5);
// array length가 5 이하일 때 pass
z.string().array().length(5);
// array length가 5일 때만 pass
Promise
Zod를 통해 promise type역시 validation 가능하다. 일반 value validation과는 다르게 검사하는 value가 promise instance여야 한다.
const testSchema = z.promise(z.string());
testSchema.parse(Promise.resolve("test"));
// validation pass
testSchema.parse("test");
// validation error
InstanceOf
Zod를 통해 검사하는 value가 특정 class의 instance인지 확인할 때 instanceOf method를 사용할 수 있다.
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
}
const testSchema = z.instanceof(Person);
const testVal = new Person("jack");
testSchema.parse(testVal);
// validation pass
Custom Schema
만약 zod에서 제공되는 valdiation외에 custom schema를 구성해야 한다면 다음과 같이 할 수 있다. 예를 들어 아래 코드는 숫자로 이루어진 문자열 마지막에 % 기호가 포함되어 있는 value만 pass하는 custom schema의 예제이다.
const testSchema = z.custom<`${number}%`>((val) => {
return typeof val === "string" ? /^\d+%$/.test(val) : false;
});
const testPassVal = "11%";
testSchema.parse(testVal);
// validation pass
const testErrorVal = "11";
testSchema.parse(testVal);
// validation error
만약 custom schema의 validation error message를 직접 설정하고 싶다면 custom의 두 번째 인자로 error message를 전달해준다.
const testSchema = z.custom<`${number}%`>((val) => {
return typeof val === "string" ? /^\d+%$/.test(val) : false;
}, "test custom error");
![[ 살펴보기 ] RDB - Relationships](https://cdn.hashnode.com/res/hashnode/image/upload/v1739711556668/48dc9e84-a621-42aa-9c9f-5fc5c436f0ec.jpeg)
![[ 살펴보기 ] MySQL - Data types](https://cdn.hashnode.com/res/hashnode/image/upload/v1739593589113/530f8704-4d27-42c9-a451-bb5c63150b99.jpeg)
![[ 살펴보기 ] TypeORM - Transactions, Migration](https://cdn.hashnode.com/res/hashnode/image/upload/v1739106042581/980b8133-61d4-406a-a026-65be9c28eace.jpeg)
![[ 살펴보기 ] TypeORM - Relations](https://cdn.hashnode.com/res/hashnode/image/upload/v1738666874402/b688bd0b-b6bb-4f43-87d8-c1b46b59f1b7.jpeg)
![[ 살펴보기 ] TypeORM - Basics](https://cdn.hashnode.com/res/hashnode/image/upload/v1738666803591/bef5df17-7dc7-4123-ae55-004d5042df39.jpeg)