Published on

Zod: TypeScript용 스키마 선언 및 검증 라이브러리

Authors
  • avatar
    Name
    Inhwan Cho
    Twitter

Zod는 TypeScript 환경에서 데이터의 스키마를 선언하고 검증할 수 있는 강력한 라이브러리입니다.

이를 통해, 데이터의 타입을 명확하게 정의하고 런타임에 해당 타입에 맞는지 검증할 수 있습니다.

Zod는 특히 API 응답이나 사용자 입력과 같이 외부로부터 들어오는 데이터의 유효성을 검사하는 데 유용하게 사용됩니다.

기본 사용 방법

1. 스키마 정의

검증하고자 하는 데이터의 형태를 정의하는 스키마를 먼저 만듭니다.

스키마(schema)는 데이터의 구조, 형식, 그리고 제약사항을 명시하는 설계도입니다.

스키마 정의 예시 -

const userSchema = z.string().min(5).max(10)

2. 데이터 검증

.parse() 또는 .safeParse() 메서드를 활용하여 데이터를 검증합니다.

이는 if문이나 try/catch문법을 사용하는 것보다 편리하고 강력합니다.

parse()와 safeParse()의 차이

  • parse() : 데이터가 스키마와 일치하지 않으면 오류를 발생시킵니다.

  • safeParse() : 데이터가 스키마와 일치하지 않아도 오류를 발생시키지 않고, 성공 여부를 나타내는 객체(Object)를 반환합니다.

데이터 검증 예시 -

// 검증 실패 시 에러 발생
const result = userSchema.parse('유저네임123') 
// 검증 실패 시 객체 생성(오류는 발생하지 않음)
const result = userSchema.safeParse('유저네임123') 

기본적인 사용 예시

import { z } from 'zod'

// 사용자 스키마 정의
const UserSchema = z.object({
  name: z.string(),
  age: z.number(),
  email: z.string().email(),
})

// 검증할 데이터
const userData = {
  name: 'John Doe',
  age: 30,
  email: 'john.doe@example.com',
}

try {
  // 데이터 검증
  const user = UserSchema.parse(userData)
  console.log('Valid user data:', user)
} catch (error) {
  console.error('Invalid user data:', error)
}

Next.js에서 서버 액션을 통한 폼 데이터 검증 예시

app/somefoler/action.ts
const checkUsername = (username) => !username.includes('something')
const confirmPasswords = ({password, confirm_password}) => password === confirm_password
// At least one uppercase letter, one lowercase letter, one number and one special character
const passwordRegex = new RegExp(
/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).+$/
);

const formSchema = z.object({
  usename:z.string({invalid_type_error:'타입 에러',required_error:'입력 안함'})
  .min(4,'4개보다 더 입력해주세요').max(10,'더 짧게 입력해주세요')
  .trim().toLowerCase()//trim을 사용하여 공백 제거, 
  .refine(checkUsername, 'something이 들어가면 안됩니다'),
  email:z.string().email(),
  password:z.string().min(10).regex(passwordRegex,'
  소문자, 대문자, 숫자, 특수문자가 1개 이상씩 필요합니다.'),
  confirm_password:z.string().min(10).regex(passwordRegex),
}).refine(confirmPasswords, {message:'비밀번호가 동일하지 않습니다', path:['confirm_password']})


export async funtion createAccount(prevState, formData){
  const data = {
    usename:FormData.get('username'),
    email:FormData.get('email'),
    password:FormData.get('password'),
    confirm_password:FormData.get('confirm_password'),
  }
  const result = formSchema.safeParse(data)
  if (!result){
    return result.error.flatten()
  } else {
    console.log(result.data)
  }
}
app/create/page.tsx
// 서버 액션을 사용한 예시
const [state, dispatch] = useFormState(createAccount, null)
...

<Input ... name='username' errors={state?.fieldErrors.username}/>
<Input ... name='email' errors={state?.fieldErrors.email}/>
<Input ... name='password' errors={state?.fieldErrors.password}/>
<Input ... name='confirm_password' errors={state?.fieldErrors.confirm_password}/>

-에러 ex. 비밀번호가 동일하지 않은 경우 등은 formError로 출력 또는 path를 설정.
// {state?.formErrors.map((error,index) => (<div key={index}>{error}</div>))}

phoneNumber와 같은 특정 검증 로직이 필요할 경우

npm install validator
npm install -D @types/validator
  • form 태그로 넘어오는 데이터는 숫자로 입력해도 string으로 변환되어 옵니다.
  • 이를 숫자 타입으로 변경하기 위해서는 몇 가지 방법이 있지만 z.coerce().number()를 사용하면 편리합니다.
app/somefoler/action.ts
import { z } from "zod";
import validator from "validator";

const phoneSchema = z.string().trim().refine(validator.isMobilePhone)
// number로 변경할 경우 min, max를 잘 설정해야 합니다.
const tokenSchema = z.coerce().number().min(100000).max(999999)

참고

zod 공식 문서