본문 바로가기

Library

zod 쓸까? 말까? - zod를 알아보자

 

zod는 javascript, typescript에서 사용가능한 유효성 검사? 스키마 정의 라이브러리이다

굳이 써야해?

내가 처음 zod를 보고 든 생각은 javascript나 typescript에서는 굳이 라이브러리를 사용하지 않고도 유효성 검사를 할 수 있는데 굳이?라는 생각이 들었다

 

근데 사람들은 왜 zod를 많이 사용할까?라고 생각하니 사용하기 간편하고 또 라이브러리 용량이 적기 때문이라고 생각한다

 

직접 코드로 구현할 수 있으나 코드가 너무 많아지고 또 라이브러리를 사용할 때는 라이브러리에서 제공하는 기능을 다 사용 못한다면 비효율적이겠지만 zod는 라이브러리 용량도 적어서 그런 부분에 대한 걱정을 안 해도 된다고 생각한다

 

사용하기!

설치하기

npm install zod
yarn add zod
pnpm add zod

 

타입 스키마 정의하기

import { z } from "zod";

const myStringSchema = z.string();

const myObjectSchema = z.object({
  name: z.string(),
  age: z.number(),
});

 

위의 첫번째 변수에는 값으로 string, 문자열만 받겠다고 스키마를 정의한 것이고,

두번째 변수에는 object, 객체를 받고 그 객체 안에는 string type의 name과 number type의 age를 받겠다고 정의한 것이다

 

스키마 검사하기

myStringSchema.parse("hihi"); // "hihi:

myObjectSchema.safeParse({ name: "gaon", age: 18 }); // {success: true, data: {…}}

myStringSchema.safeParse(10); // {success: false, error: {…}}

myObjectSchema.safeParse({ name: "gaon", age: "18" }); // zodError

 

정의한 스키마와 내가 받은 값이 대응하는지를 parse와 safeParse 메서드를 통해 검사할 수 있다

 

js, ts로 parse를 비슷하게 구현해본다면 이렇게 될 것이다

// js 
export const parse = (value) => {
  if (typeof value != "string") throw new Error("invalid type");

  return value;
};

// ts
export const parse = (value: any) => {
  if (typeof value != "string") throw new Error("invalid type");

  return value;
};

 

아무튼 parse와 safeParse로 스키마를 검사하는데 parse와 safeParse가 같은 기능을 하는거 아니야?라는 생각이 들수도 있다

이둘은 스키마를 검사한다는 점에서 같아 보일수도 있는데 다른점은 에러를 던지냐 안던지냐 차이이다

추가 옵션

zod는 타입 검사말고도 값의 길이, 형식등을 추가로 검사해줄수 있도록 지원한다

 

import { z } from "zod";

const minSchema = z.string().min(5, { message: "너무 짧아요" });

const maxSchema = z.string().max(5, { message: "너무 길어요!" });

const lengthSchema = z.string().length(5, { message: "길이가 안맞아요!" });

const emailSchema = z.string().email({ message: "이메일 형식이 아니에요!" });

const urlSchema = z.string().url({ message: "url 형식이 아니에요!" });

 

위의 스키마는 string일 경우 추가적으로 사용하는 스키마이고 다른 타입일 경우에는 다른 추가 스키마를 제공한다

 

js, ts로 구현 해본다면 이렇게 될 것이다

// js
export const maxSchema = (value) => {
  if (typeof value != "string") throw new Error("invalid type");

  if (value.length > 5) throw new Error("너무 길어요!");

  return value;
};


// ts
export const maxSchema = (value: any) => {
  if (typeof value != "string") throw new Error("invalid type");

  if (value.length > 5) throw new Error("너무 길어요!");

  return value;
};

 

타입으로 만들기!

만약 객체 스키마를 만들었다면 타입스크립트 type으로도 변환하여 사용할 수 있다

 

import { z } from "zod";

const userSchema = z.object({
  name: z.string().min(2).max(4),
  age: z.number().positive(),
});

export type User = z.infer<typeof userSchema>; // {name: string, age: number}

 

type으로 변환하여 사용해보면서 알게 된것이 있는데

tsconfig 설정에서 strict 설정을 true로 설정해주지 않으면 값의 타입이 필수요소가 아니게 된다
그러므로 tsconfig 설정에서 strict 설정을 true로 해주도록 하자!

react-hook-form과 같이 사용하기

zod를 사용하는 가장 큰 이유중 하나는 react-hook-form과 같이 사용할 수있기 때문일 것이다 - react-hook-form 알아보기

 

zod와 react-hook-form을 같이 사용하려면 @hookform/resolvers를 설치해줘야 한다

npm install @hookform/resolvers
yarn add @hookform/resolvers
pnpm add @hookform/resolvers

 

import { zodResolver } from "@hookform/resolvers/zod";
import { SubmitErrorHandler, SubmitHandler, useForm } from "react-hook-form";
import { z } from "zod";

const genderArr = ["male", "female", "anynmous"] as const;

const userSchema = z.object({
  name: z.string().min(2).max(4),
  age: z
    .string()
    .refine((value) => !Number.isNaN(Number(value)), {
      message: "숫자가 아닙니다",
    }),
  gender: z
    .enum(["성별을 선택해주세요", ...genderArr])
    .refine((value) => value !== "성별을 선택해주세요", {
      message: "기본 값을 선택하면 안됩니다",
    }),
});

type User = z.infer<typeof userSchema>;

const formPage = () => {
  const { register, handleSubmit } = useForm<User>({
    resolver: zodResolver(userSchema),
    defaultValues: { name: "" },
  });

  const onSubmit: SubmitHandler<User> = (data) => {
    console.log(data);
  };

  const onError: SubmitErrorHandler<User> = (error) => {
    console.log(error);
  };

  return (
    <div>
      <form onSubmit={handleSubmit(onSubmit, onError)}>
        <input {...register("name")} />
        <input {...register("age")} type="number" />
        <select {...register("gender")} defaultValue="성별을 선택해주세요">
          <option value="성별을 선택해주세요" hidden selected>
            성별을 선택해주세요
          </option>
          {genderArr.map((i) => (
            <option value={i} key={i}>
              {i}
            </option>
          ))}
        </select>
        <button>제출</button>
      </form>
    </div>
  );
};

export default formPage;

 

위와 같이 작성을 했는데 기존 react-hook-form을 사용한 form 코드와 다른점으로는 resolver에 zodResolver를 할당한 것이다

 

마무리

이렇게 zod를 왜 사용하는지, 사용법과 react-hook-form과 같이 사용하는 법을 알아봤다

zod를 처음 알게된 것은 학교 선배님들과 프로젝트를 하면서 코드만 본것이었는데 이번기회에 알게되어 프로젝트의 코드의 이해도가 올라갔다고 생각한다

그리고 zod 공부하면서 zod 공식문서를 많이 참고했는데 설명이 아주 잘 되어있다고 느꼈다

728x90