
단순한 데이터 검증을 넘어, 사용자 입력을 관리할 때.
React Hook Form과 Zod를 함께 사용하면 코드의 양은 크게 줄어들고, 타입 안정성은 극대화될 수 있습니다..
1. 시작, 필수 패키지 설치
React Hook Form과 Zod를 연결해주는 어댑터인 @hookform/resolvers 추가로 설치.
npm install react-hook-form zod @hookform/resolvers
2. 왜 이 조합?
기본적인 React Hook Form의 register 방식은 검증 로직이 JSX 안에 파편화되어 유지보수가 어려움.
하지만 Zod를 연동하면 하기의 장점이 있습니다.
- 비즈니스 로직의 분리: 검증 로직(Schema)을 컴포넌트 외부에서 관리할 수 있다.
- 강력한 타입 추론: 폼에 입력된 데이터의 타입을 별도로 정의할 필요가 없다.
- 일관된 에러 메시지: 복잡한 조건부 렌더링 없이 Zod 스키마에 정의된 에러 메시지를 그대로 출력할 수 있다.
3. 실전 예제: 회원가입 폼 구현
가장 전형적인 회원가입 시나리오를 통해 연동 방법을 살펴보겠습니다.
① 스키마 정의하기
먼저 폼의 구조와 검증 규칙을 정의.
import { z } from "zod";
export const signupSchema = z.object({
email: z.string().email("유효한 이메일을 입력해주세요."),
password: z
.string()
.min(8, "비밀번호는 최소 8자 이상이어야 합니다.")
.regex(/[A-Z]/, "최소 하나의 대문자가 포함되어야 합니다."),
confirmPassword: z.string(),
}).refine((data) => data.password === data.confirmPassword, {
path: ["confirmPassword"],
message: "비밀번호가 일치하지 않습니다.",
});
// 스키마를 통해 타입 추출
export type SignupInput = z.infer<typeof signupSchema>;
② 컴포넌트에 적용하기
useForm의 resolver 옵션에 zodResolver를 넘겨줌.
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
const SignupForm = () => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<SignupInput>({
resolver: zodResolver(signupSchema), // Zod 연동
mode: "onChange", // 실시간 검증 활성화
});
const onSubmit = (data: SignupInput) => {
console.log("제출된 데이터:", data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<input {...register("email")} placeholder="이메일" />
{errors.email && <p>{errors.email.message}</p>}
</div>
<div>
<input type="password" {...register("password")} placeholder="비밀번호" />
{errors.password && <p>{errors.password.message}</p>}
</div>
<div>
<input type="password" {...register("confirmPassword")} placeholder="비밀번호 확인" />
{errors.confirmPassword && <p>{errors.confirmPassword.message}</p>}
</div>
<button type="submit">회원가입</button>
</form>
);
};
4. 요약
- zodResolver: React Hook Form이 유효성 검사를 할 때 Zod 스키마를 사용하도록 징검다리 역할.
- errors 객체: Zod 스키마에서 정의한 message가 자동으로 errors.[field].message에 매핑.
- Type Safety: useForm<SignupInput>처럼 제네릭을 넘겨주면, register 함수를 사용할 때 오타를 내거나 잘못된 필드명을 입력하는 것을 방지해줌.
마치며
React Hook Form과 Zod의 조합은 이제 현대적인 React 개발에서 표준에 가깝게 자리 잡았고.
폼이 복잡해질수록 이 조합이 주는 생산성은 더욱 커집니다. 단순히 값을 검사하는 것을 넘어, 서버로 보내기 전 데이터의 '최종 검증'에 최적의 조합입니다.