FE/React

[React Hook Form + Zod] 실무에서 쓰는 폼 검증 완전 정리 (yup과의 차이까지!)

yhkim_ 2025. 5. 8. 16:55
로그인, 회원가입, 게시글 작성 등 사용자 입력이 필요한 화면에서는 폼 유효성 검사가 필수입니다.

React 프로젝트에서 가장 많이 쓰이는 폼 라이브러리는 `React Hook Form`,  
그리고 유효성 검증 라이브러리는 `Zod` 또는 `Yup`입니다.

 

실무에서는 이렇게 씁니다.

React Hook Form은 폼 상태 관리(에러, 입력 값 등)
Zod or Yup은 유효성 검증 스키마

 

 


1. React Hook Form 이란?

React에서 가볍고 빠른 폼 상태 관리를 할 수 있게 해주는 라이브러리

 

  • useForm 훅으로 간편하게 폼 상태 관리
  • 불필요한 리렌더링 최소화 (성능 우수)
  • Yup, Zod, Joi 등과 함께 사용 가능
npm install react-hook-form

 

 

 

2. Zod 란?

zod는 타입스크립트를 기반으로 한 런타임 데이터 유효성 검사(validation) 라이브러리입니다.

입력 데이터가 예상한 형태인지 검사하고, 동시에 타입도 추론해주는 강력한 도구입니다.

 

  • 스키마를 기반으로 자동 타입 추론 가능
  • 런타임에서도 타입 안전성 보장
  • React Hook Form과 궁합이 매우 좋음
npm install zod @hookform/resolvers

 

 

📝 기본 개념

z.object()란?

z.object()는 객체 스키마(schema)를 정의하는 함수입니다.

즉, 객체 형태의 데이터가 어떤 속성을 가져야하고, 각 속성이 어떤 타입인지 명시합니다.

 

※ 스키마(schema)란? 
데이터의 구조와 규칙을 정의한 청사진입니다.
쉽게 말하면, "이 데이터는 어떤 모양이어야 하고, 어떤 조건을 만족해야 하는지 미리 정해놓은 설계도"

 

예제1 : 간단한 객체 스키마

import { z } from "zod";

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

 

-> 다음과 같은 타입을 검증합니다.

const validUser = {
  name: "Alice",
  age: 25,
};

const invalidUser = {
  name: "Bob",
  age: "25", // ❌ 에러: age는 number여야 함
};

 

 

타입 자동 추론(z.infer)

Zod은 타입도 자동으로 추론해줍니다.

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

조건 추가하기

Zod 스키마는 조건도 붙일 수 있습니다.

const loginSchema = z.object({
  email: z.string().email("이메일 형식이 올바르지 않아요."),
  password: z.string().min(8, "비밀번호는 최소 8자리"),
});

.refine()로 커스텀 조건 추가

const passwordSchema = z
  .string()
  .min(8)
  .refine((val) => /[A-Z]/.test(val), {
    message: "대문자를 포함해야 합니다.",
  });

 

 

 

📌 Zod  vs Yup 비교

실무에서는
- TypeScript 기반 프로젝트라면 Zod,
- JavaScript-only라면 Yop을 많이 씀.
항목 Zod Yup
타입 추론 ✅ 자동 추론 (Zod만 가능) ❌ 타입 수동 지정 필요
TS 친화도 ✅ 매우 좋음 보통
체이닝 문법 ✅ 명시적, 가독성 높음 ✅ 자연스럽고 직관적
문법 스타일 함수 기반 (z.string()) 체이닝 기반 (yup.string().required())
오류 메시지 커스터마이징 쉽고 명확함 가능하지만 비교적 복잡
유지보수 상태 활발 (Zod 인기 급상승) 안정적이나 다소 정체됨

 

 

3. 예제 : 로그인 폼(React Hook Form + Zod)

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

const loginSchema = z.object({
  email: z.string().email("올바른 이메일 형식이 아닙니다."),
  password: z.string().min(6, "비밀번호는 최소 6자 이상이어야 합니다."),
});

type LoginFormData = z.infer<typeof loginSchema>;

export default function LoginForm() {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm<LoginFormData>({
    resolver: zodResolver(loginSchema),
  });

  const onSubmit = (data: LoginFormData) => {
    console.log("Login 성공", data);
    // API 호출
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
      <input type="text" placeholder="Email" {...register("email")} />
      {errors.email && <p>{errors.email.message}</p>}

      <input type="password" placeholder="Password" {...register("password")} />
      {errors.password && <p>{errors.password.message}</p>}

      <button type="submit">Login</button>
    </form>
  );
}