zod를 사용하여 선언적으로 데이터 검증하기

TypeScript
zod를 사용하여 선언적으로 데이터 검증하기

안녕하세요! 오늘은 선언형 데이터 검증 라이브러리zod란 무엇이며, 어떻게 사용하는지를 알아보겠습니다.

1. 기존의 데이터 검증 🤔

여러분이 진행하는 프로젝트에서 사용자로부터 **많은 입력값(회원가입, 글 작성 등등)**을 받는경우, 해당 입력값에 대해서 검증하는 절차가 작성되는 경우가 많습니다.

예를들어, 아래의 규칙을 따르는 데이터가 있다고 가정해봅시다.

/*
  email: 필수 입력, 이메일 정규식 맞춰야함
  name: 필수 입력
  password: 필수 입력, 비밀번호 정규식 맞춰야함
  interestCategories: 2개 이상, 5개 이하로 선택해야함
*/

interface SignUpDto {
  email: string;
  name: string;
  password: string;
  interestCategories: number[];
}

위의 데이터를 검증시, 선언형 데이터 검증 라이브러리를 사용하지 않는경우에는 아래처럼 코드를 작성할 수 있습니다.

interface ValidateResult {
  succeed: boolean;
  message: string;
}

const validateSignUpDto = (signUpDto: SignUpDto): ValidateResult => {
  const { email, name, password, checkPassword, interestCategories } = signUpDto;

  if (email.trim().length <= 0) {
    return {
      succeed: false,
      message: '이메일을 입력해주세요.',
    };
  } else if (이메일 정규식에 안맞다면) {
    return {
      succeed: false,
      message: '이메일 정규식에 맞게 입력해주세요.'
    };
  } else if (name.trim().length <= 0) {
    return {
      succeed: false,
      message: '이름을 입력해주세요.',
    };
  } else if (password.trim().length <= 0) {
    return {
      succeed: false,
      message: '비밀번호를 입력해주세요.',
    };
  } else if (비밀번호 정규식에 안맞다면) {
    return {
      succeed: false,
      message: '비밀번호 정규식에 맞게 입력해주세요.',
    };
  } else if (interestCategories.length <= 2) {
    return {
      succeed: false,
      message: '관심분야를 2개 이상 선택해주세요.',
    };
  } else if (interestCategories.length > 5) {
    return {
      succeed: false,
      message: '관심분야는 5개 이하로 선택가능합니다.',
    };
  }

  return {
    succeed: true,
    message: '성공',
  };
};

위처럼 if문을 사용한 검증 함수를 만들고, React를 예시로 했을때 컴포넌트나 커스텀 훅 등에서 함수를 불러와서 데이터를 검증할 수 있습니다. 기능상으로는 문제가 없습니다.


그러나 위의 회원가입 기능에서 필요한 데이터가 추가되고, 그에따른 검증식도 늘어나면 어떻게 될까요? 검증 함수는 점점 코드량이 증가하여 가독성이 매우 떨어지게 됩니다. 이는 유지보수를 어렵게 만들 수 있는 코드가 되는것이죠.

이전까지 저도 저렇게 적었을때는 검증에 대한 코드를 작성하는것이 정말 귀찮았던 것 같습니다. 그래서 검증 코드를 잘 작성할 수 있는 방법을 찾아보다가, zod 라이브러리를 알게 되었습니다.

2. zod 검증 라이브러리 🔨

위처럼 모든 검증 과정을 작성한 코드(명령형)의 문제점을 해결하기 위해서 간결하게 선언적으로 작성 가능한 zod 라이브러리가 등장했습니다.

zod 검증 라이브러리

zod 라이브러리 공식 문서 바로가기

zod 라이브러리의 대표적인 장점을 몇가지 뽑아보자면

  1. 선언적으로 데이터 검증 (코드 간결)
  2. 타입스크립트 지원
  3. 스키마 -> 타입 변환 지원 (중복 코드 최소화)

위의 장점들을 뽑을 수 있습니다. 그렇다면 zod를 코드에서 어떻게 작성할 수 있는지 알아봅시다.

오늘 소개드릴 zod 외에도 yup, joi 등의 검증 라이브러리도 훌륭하다고 생각하기에, 한번쯤 보시는걸 추천합니다. 😃

3. 사용방법 🔍

가장 먼저 npm, yarn 등의 패키지 매니저로 zod를 설치해줍니다.

npm install zod

또는

yarn add zod

기본적으로 zod 패키지에서 z 변수를 import하여 기능을 사용할 수 있습니다.

예를들어, 데이터의 타입이 string일때는 z.string(), number일때는 z.number() 등으로 작성할 수 있습니다.

import { z } from 'zod';

const stringSchema = z.string();

const numberSchema = z.number();

3-1. 데이터 검증

zod는 선언적 데이터 검증이 주 특징입니다. 그래서 데이터 검증은 핵심기능으로도 볼 수 있습니다.

특정한 구조의 데이터를 검증하려면 zod스키마(Schema)를 선언해야합니다. 스키마에서는 필드별 데이터 타입, 규칙을 선언적으로 정의하여 구성합니다. zod에서 제공하는 규칙용 메소드들은 다양한데요. 이번 글에서는 대표적으로 많이 사용되는 메소드들만 알려드리겠습니다.


이번 글에서 설명하지 않은 검증 메소드들에 대해서는 zod 공식문서에 설명이 매우 잘되어있으니 보시는걸 추천합니다.


위에서 예시로 설명드린 회원가입 예제를 zod로 변환하여 작성해보겠습니다. 코드에 대한 설명은 주석으로 적어두었습니다.

zod는 함수형 프로그래밍 형태로 사용되는점을 미리 알아두시면 좋습니다.

import { z } from 'zod'; // zod 패키지로부터 import

// 스키마 선언
// 회원가입과 같은 정보는 일반적으로 객체형태로 관리하기에, 객체임을 나타내는 z.object()로 감싸줍니다.
export const signUpSchema = z.object({
  // 문자열 형태로 필수(1글자 이상), 올바른 이메일 형식
  email: z
    .string()
    .trim()
    .min(1, '이메일을 입력해주세요.')
    .email('올바른 이메일을 입력해주세요.'),

  // 문자열 형태로 필수 (1글자 이상)
  name: z.string().trim().min(1, '이름을 입력해주세요.'),

  // 문자열 형태로 필수(1글자 이상), 8자 이상의 알파벳으로 구성
  password: z
    .string()
    .trim()
    .min(1, '비밀번호를 입력해주세요.')
    .regex(/b[a-zA-Z]{8,}/b, '8자 이상의 알파벳으로 구성되어야 합니다.'),

  // 숫자 배열 형태로 필수, 배열 길이가 2이상, 5이하로 이루어져야함
  interestCategories: z
    .array(z.number())
    .min(2, '2개 이상의 카테고리를 선택해주세요.')
    .max(5, '5개 이하로 카테고리를 선택해주세요.'),
});

zod를 사용하면 데이터에 대한 검증 코드를 위처럼 간결하게 작성할 수 있습니다. 이전에 if문을 사용한 검증 코드와 비교했을때 정말 간결함을 알 수 있습니다.

위 코드에서 min, max, email과 같은 검증 메소드를 사용했음을 알 수 있는데요. 숫자 인자는 만족하는 데이터 길이이고, 문자열 인자는 데이터를 만족하지 않을때 설정할 오류 메세지입니다.


이제 위의 스키마를 사용하여 데이터를 검증할 수 있는데요. 검증을 하는 방법은 대표적으로 parse 또는 safeParse 메소드를 사용하여 검증할 수 있습니다.

parse: 검증 오류시, throw Error (try / catch 처리 필요) safeParse: 검증 오류시 오류의 정보를 담는 객체 반환

해당 메소드에 넘긴 데이터가 검증식에 맞지 않을경우, 실패합니다.

try {
  signUpSchema.parse({
    email: 'asdf@gmail.com',
    name: '권용빈',
    password: 'asdfasdf',
    interestCategories: [2], // 카테고리를 한개만 전달했기에, catch문으로 넘어간다. (오류)
  });
} catch (error) {
  // ZodError
}

// 올바른 형태의 데이터 전달, 성공
const parsed = signUpSchema.safeParse({
  email: 'asdf@gmail.com',
  name: '권용빈',
  password: 'asdfasdf',
  interestCategories: [2, 3, 4],
});

console.log(parsed.success); // true

// 올바르지 않은 형태의 데이터 전달, 실패
const parsed2 = signUpSchema.safeParse({
  email: 'asdf', // 올바르지 않은 이메일 형식
  name: '권용빈',
  password: 'asdfasdf',
  interestCategories: [2, 3, 4],
});

if (!parsed.success) {
  console.log(parsed.error.errors[0].message); // 가장 첫번째 오류의 메세지 출력
}

safeParse 메소드의 success 필드가 false로 추론될때만 error 필드에 접근이 가능합니다.

3-2. 스키마 -> 타입 변환

이번에는 zod로 선언한 스키마를 타입스크립트의 타입으로 변환하는 방법을 알아보겠습니다. 검증 스키마를 선언한 정보를 타입으로 변환이 가능하다면, 필드 정보를 중복으로 적을일이 없기때문에 개발자 입장에서 더 편합니다.


타입으로 변환하는 방법은 z.infer 타입을 사용하여 변환할 수 있습니다.

import { z } from 'zod';

export type SignUpDto = z.infer<typeof signUpSchema>;

/*
  email: string;
  name: string;
  password: string;
  interestCategories: number[];
*/

4. 마치며 📌

오늘은 zod 라이브러리란 무엇이며, 어떻게 사용할 수 있는지를 간단하게 알아보았습니다.

저는 zod를 도입해보면서 코드를 더 간결하게 줄일 수 있어서 매우 좋았었는데요. 데이터 검증코드에 대해 고민이 있으시다면 한번 사용해보시는것을 추천합니다.


이상으로 글을 마치겠습니다. 읽어주셔서 감사합니다!

블로그 개선 (1) - 글 파일 구조 및 이미지 개선
이전글

블로그 개선 (1) - 글 파일 구조 및 이미지 개선

최근 제 블로그의 글 파일 구조 및 이미지를 개선한 경험을 공유합니다.

Array.fill 메소드를 사용할때의 주의점
다음글

Array.fill 메소드를 사용할때의 주의점

배열을 특정한 값으로 채울때 사용할 수 있는 Array.fill 메소드의 주의점에 대해서 알아봅시다.