개발여행

zod 라이브러리로 프론트에서 데이터 구조 검증하기 본문

Frontend

zod 라이브러리로 프론트에서 데이터 구조 검증하기

jbilee 2024. 7. 29. 01:19

zod는 자바스크립트에서 사용할 수 있는 데이터 스키마 검증 라이브러리다. 데이터의 유효성을 확인해주는 것뿐만 아니라, 유효하지 않을 경우 스키마와 일치하지 않는 부분에 대해 내가 지정한 오류 메세지가 담긴 객체를 알아서 반환해주기도 한다. 폼 데이터 검증 등의 상황에서 검증 로직을 간편하게 구현할 수 있도록 도와준다.

 

zod를 사용하면 크게 스키마 정의 → 정의한 스키마를 토대로 데이터 검증 → 검증 성공/실패 여부에 따라 처리 플로우로 로직을 구현할 수 있다.

 

스키마 정의하기

데이터가 올바른 형식으로 들어왔는지 확인하려면 일단 비교할 스키마가 필요하다. 스키마는 라이브러리에서 z를 import한 다음 정의하고 싶은 데이터 타입을 선언해 만들 수 있다.

 

이때 다양한 조건을 덧붙여서 내가 원하는 데이터가 정확히 어떤 범위에 해당하는 데이터인지를 명시할 수 있다. 문자열이라면 길이에 제한을 둘 수도 있고, 숫자라면 내가 지정한 최소값이나 최대값 범위 안에 해당하는지 등등을 체크할 수 있다.

 

예시로 로그인 폼 컴포넌트에 사용되는 필드로 스키마를 정의한다면 아래처럼 정의할 수 있다.

import { z } from "zod";

export const LoginFormSchema = z.object({
  username: z.string().min(1, { message: "* 아이디를 입력해 주세요." }),
  password: z.string().min(1, { message: "* 비밀번호를 입력해 주세요." }),
});

 

예시처럼 문자열 타입인지 확인하는 z.string() 뒤에 .min()을 추가해서 최소 1자 이상의 문자열이여야 함을 강제할 수 있다. 검증에 실패했을 때 출력할 에러 메세지는 { message: "에러 메세지" } 객체를 검증 함수에 두번째 인자로 전달하면 된다.

 

스키마로 데이터 검증하기

내가 정의한 스키마에 실제로 데이터를 대조해볼 땐 safeParse() 함수를 사용한다. 데이터를 검증하는 함수로는 parse()와 safeParse()가 있는데, parse()를 사용하지 않는 이유는 검증에 실패해서 예외가 발생했을 때 함수 내에서 catch해주지 않기 때문이다. 반면 safeParse()는 예외 발생으로 끝나지 않고, 어디서 문제가 발생했는지를 객체에 넣어서 결과로 반환해준다.

import { LoginFormSchema } from "@/schema/login";

export async function login(credentials: { username: string; password: string }) {
  const validatedForm = LoginFormSchema.safeParse(credentials);
}

 

파싱 함수가 성공했을 경우 { success: true, data } 객체를 반환하고, data에는 검증할 때 전달했던 데이터가 들어있다. 실패했을 경우엔 { success: false, error } 객체를 반환하는데, 이때 error를 그대로 사용할 수는 없고 따로 usable한 포맷으로 가공해야 한다.

 

에러 메세지 추출하기

safeParse()로 데이터를 검증했을 때 검증에 실패하면 error attribute가 포함된 객체가 반환된다. 검증 성공/실패 여부를 success attribute에 boolean 값으로 알려주니 result.success 값이 false인 경우 error의 값을 반환하는 방식으로 로직을 짤 수 있다.

 

에러 메세지는 파싱 함수가 반환한 객체에 .flatten() 메소드를 실행해 fieldErrors 값을 가져와야 한다.

import { LoginFormSchema } from "@/schema/login";

export async function login(credentials: { username: string; password: string }) {
  const validatedForm = LoginFormSchema.safeParse(credentials);
  
  // 검증에 성공한 경우 성공 로직 진행
  if (validatedForm.success) {
    // ...
  }
  
  // 검증에 실패한 경우 error 내용을 반환
  return { isValid: false, errors: validatedForm.error?.flatten().fieldErrors };
}

 

위 예시의 LoginFormSchema를 사용했을 때, 사용자가 username 필드와 password 필드를 전부 빈칸으로 뒀다면 validatedForm.error.flatten().fieldErrors로 에러 객체를 추출했을 때 아래 값이 나온다.

{
  "username": ["* 아이디를 입력해 주세요."],
  "password": ["* 비밀번호를 입력해 주세요."]
}

 

에러 객체의 value는 항상 Array<string> 타입이기 때문에 폼 컴포넌트에서 타입을 선언할 때 이 점을 유의해야 한다. LoginFormSchema에서는 한 필드에서 한가지 조건만 확인하지만, 만약 조건이 여러개 붙어있다면 검증에 실패한 단계부터 마지막 단계까지의 에러 메세지가 전부 배열에 반환된다.

 

조건이 하나 이상일 경우의 예시로는 회원가입 폼에서 이메일을 입력 받을 때가 있다.

import { z } from "zod";

export const SignupFormSchema = z.object({
  email: z
    .string()
    .min(1, { message: "* 이메일을 입력해 주세요." })
    .email({ message: "* 이메일 형식이 아닙니다." }),
  // ...
});

 

위 같은 경우 email 인풋 필드가 비어있다면 에러 객체에는 두가지 에러 메세지가 배열로 전달된다: ["* 이메일을 입력해 주세요.", "* 이메일 형식이 아닙니다."]

만약 사용자가 뭔가를 입력했지만 이메일 형식이 아니라면 배열 안에 한가지 에러 메세지만 반환된다: ["* 이메일 형식이 아닙니다."]

 

검증에 실패한 경우 zod가 반환하는 에러 객체에는 실패 이유가 하나 이상일 수 있기 때문에 스키마를 정의할 땐 가장 기본적으로 확인할 사항부터 검증해서, 사유가 여럿일 경우에 일반적으로 맨 처음 검증에 실패한 이유를 보여주도록 설계하면 적절할 것 같다.

'Frontend' 카테고리의 다른 글

FSD 아키텍쳐 알아보기  (0) 2024.08.01
ARIA란?  (0) 2024.07.31
Nuxt 시작하기  (2) 2024.07.16
Konva 배우기 (1)  (1) 2024.07.12
Vue 달력 라이브러리 v-calendar의 버그와 대안책  (0) 2024.07.10