React Hook Form과 zod를 활용한 폼 구현
React Hook Form과 zod를 활용한 폼 구현

React Hook Form과 zod를 활용한 폼 구현

요약
React Hook Form과 zod를 사용한 폼 만들기
작성일
Feb 4, 2025
태그
React
Hooks
Zod

React Hook Form과 Zod로 폼 구현

최근 진행한 프로젝트에서 하나의 페이지에서 제출할 폼 데이터가 많은 프로젝트를 진행했습니다. 초기에는 useState를 활용하여 폼 데이터를 관리했지만 폼이 많아질수록 관리가 복잡해지고 상태 업데이트 코드가 길어지는 문제가 발생했습니다.
이 문제를 해결하기 위해 React Hook FormZod를 활용하여 보다 효율적으로 폼을 관리하는 방법을 적용했습니다. 이번 글에서는 그 과정을 공유하고자 합니다.

React Hook Form과 Zod란?

React Hook Form

react-hook-form은 React에서 폼을 쉽게 다룰 수 있도록 도와주는 라이브러리입니다.
  • 불필요한 렌더링을 최소화하여 성능 최적화
  • ref를 이용한 네이티브 폼 핸들링 방식 지원
  • useForm 훅을 통해 간결한 코드 작성 가능

Zod

zod는 TypeScript 기반의 스키마 검증 라이브러리로 폼 데이터의 유효성을 검사하는 데 유용합니다.
  • 직관적인 스키마 정의
  • 타입 검증 및 런타임 유효성 검사 가능
  • react-hook-form과의 강력한 연동 지원

React Hook Form과 Zod를 이용한 폼 구현

1. 설치

먼저, react-hook-form@hookform/resolvers, zod를 설치합니다.
npm install react-hook-form @hookform/resolvers zod

2. 폼 컴포넌트 구현

리액트 훅 폼과 Zod를 사용하면 아래와 같이 폼 컴포넌트를 구현할 수 있습니다.
코드 설명
  • useForm을 사용하여 폼을 초기화하고 resolverzodResolver(formSchema)를 지정하여 유효성 검사를 적용했습니다.
  • register를 이용해 각 입력 필드를 react-hook-form과 연결했습니다.
  • 오류 메시지는 formState.errors 객체에서 가져와 표시했습니다.
  • handleSubmit 함수를 사용하여 폼 제출 시 검증된 데이터를 처리합니다.
import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; const formSchema = z.object({ name: z.string().min(2, "이름은 최소 2자 이상이어야 합니다."), email: z.string().email("올바른 이메일 형식을 입력하세요."), age: z.number().min(18, "18세 이상만 가입 가능합니다.").max(99, "99세 이하만 가입 가능합니다."), }); type FormValues = z.infer<typeof formSchema>; export default function UserForm() { const { register, handleSubmit, formState: { errors }, } = useForm<FormValues>({ resolver: zodResolver(formSchema), }); const onSubmit = (data: FormValues) => { console.log("폼 데이터:", data); }; return ( <form onSubmit={handleSubmit(onSubmit)} className="space-y-4"> <div> <label>이름:</label> <input {...register("name")} className="border p-2" /> {errors.name && <p className="text-red-500">{errors.name.message}</p>} </div> <div> <label>이메일:</label> <input {...register("email")} className="border p-2" /> {errors.email && <p className="text-red-500">{errors.email.message}</p>} </div> <div> <label>나이:</label> <input type="number" {...register("age", { valueAsNumber: true })} className="border p-2" /> {errors.age && <p className="text-red-500">{errors.age.message}</p>} </div> <button type="submit" className="bg-blue-500 text-white p-2">제출</button> </form> ); }
 
 

참고문헌