React Hook Form 완벽 가이드 | 폼 관리·Validation·Zod·성능·실전 활용
이 글의 핵심
React Hook Form 완벽 가이드에 대해 정리한 개발 블로그 글입니다. React Hook Form으로 효율적인 폼을 구현하는 완벽 가이드입니다. register, handleSubmit, Validation, Zod 통합, 성능 최적화까지 실전 예제로 정리했습니다. > 실무 경험 공유:… 개념과 예제 코드를 단계적으로 다루며, 실무·학습에 참고할 수 있도록 구성했습니다. 관련…
이 글의 핵심
React Hook Form으로 효율적인 폼을 구현하는 완벽 가이드입니다. register, handleSubmit, Validation, Zod 통합, 성능 최적화까지 실전 예제로 정리했습니다.
실무 경험 공유: Formik에서 React Hook Form으로 전환하면서, 리렌더링이 90% 감소하고 폼 성능이 크게 향상된 경험을 공유합니다.
들어가며: “폼이 느려요”
실무에서 마주치는 문제들
리렌더링이 많아요
Controlled input은 느립니다. React Hook Form은 Uncontrolled로 빠릅니다. Validation이 복잡해요
수동 검증은 번거롭습니다. React Hook Form은 선언적으로 검증합니다. 타입 안전성이 부족해요
런타임 에러가 발생합니다. Zod 통합으로 타입 안전성을 확보할 수 있습니다.
1. React Hook Form이란?
핵심 특징
React Hook Form은 고성능 폼 라이브러리입니다. 주요 장점
- 빠른 성능: 최소 리렌더링
- 간단한 API: 직관적인 문법
- Validation: 내장 검증
- TypeScript: 완벽한 지원
- 작은 크기: 9KB
2. 설치 및 기본 사용
설치
npm install react-hook-form
기본 폼
import { useForm } from 'react-hook-form';
interface FormData {
email: string;
password: string;
}
export default function LoginForm() {
const { register, handleSubmit, formState: { errors } } = useForm<FormData>();
const onSubmit = (data: FormData) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register('email', { required: 'Email is required' })} />
{errors.email && <span>{errors.email.message}</span>}
<input
type="password"
{...register('password', {
required: 'Password is required',
minLength: {
value: 8,
message: 'Password must be at least 8 characters',
},
})}
/>
{errors.password && <span>{errors.password.message}</span>}
<button type="submit">Login</button>
</form>
);
}
3. Validation
내장 Validation
<input
{...register('email', {
required: 'Email is required',
pattern: {
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: 'Invalid email address',
},
})}
/>
<input
{...register('age', {
required: true,
min: { value: 18, message: 'Must be 18+' },
max: { value: 120, message: 'Invalid age' },
})}
type="number"
/>
커스텀 Validation
<input
{...register('username', {
required: true,
validate: async (value) => {
const exists = await checkUsernameExists(value);
return !exists || 'Username already taken';
},
})}
/>
4. Zod 통합
설치
npm install @hookform/resolvers zod
사용
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
const schema = z.object({
email: z.string().email('Invalid email'),
password: z.string().min(8, 'Password must be at least 8 characters'),
age: z.number().min(18, 'Must be 18+'),
});
type FormData = z.infer<typeof schema>;
export default function SignupForm() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<FormData>({
resolver: zodResolver(schema),
});
const onSubmit = (data: FormData) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register('email')} />
{errors.email && <span>{errors.email.message}</span>}
<input type="password" {...register('password')} />
{errors.password && <span>{errors.password.message}</span>}
<input type="number" {...register('age', { valueAsNumber: true })} />
{errors.age && <span>{errors.age.message}</span>}
<button type="submit">Sign Up</button>
</form>
);
}
5. Watch & Control
watch
const { register, watch } = useForm();
const email = watch('email');
const allValues = watch();
useEffect(() => {
console.log('Email changed:', email);
}, [email]);
setValue
const { register, setValue } = useForm();
const handleReset = () => {
setValue('email', ');
setValue('password', ');
};
6. Controller (커스텀 컴포넌트)
import { Controller } from 'react-hook-form';
import Select from 'react-select';
<Controller
name="country"
control={control}
rules={{ required: true }}
render={({ field }) => (
<Select
{...field}
options={[
{ value: 'us', label: 'United States' },
{ value: 'kr', label: 'South Korea' },
]}
/>
)}
/>
7. 배열 필드
import { useFieldArray } from 'react-hook-form';
export default function DynamicForm() {
const { register, control, handleSubmit } = useForm({
defaultValues: {
items: [{ name: ', quantity: 0 }],
},
});
const { fields, append, remove } = useFieldArray({
control,
name: 'items',
});
return (
<form onSubmit={handleSubmit((data) => console.log(data))}>
{fields.map((field, index) => (
<div key={field.id}>
<input {...register(`items.${index}.name`)} />
<input type="number" {...register(`items.${index}.quantity`)} />
<button type="button" onClick={() => remove(index)}>
Remove
</button>
</div>
))}
<button type="button" onClick={() => append({ name: ', quantity: 0 })}>
Add Item
</button>
<button type="submit">Submit</button>
</form>
);
}
8. 실전 예제: 복잡한 폼
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
const schema = z.object({
personalInfo: z.object({
firstName: z.string().min(2),
lastName: z.string().min(2),
email: z.string().email(),
}),
address: z.object({
street: z.string(),
city: z.string(),
zipCode: z.string().regex(/^\d{5}$/),
}),
preferences: z.object({
newsletter: z.boolean(),
notifications: z.boolean(),
}),
});
type FormData = z.infer<typeof schema>;
export default function ComplexForm() {
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm<FormData>({
resolver: zodResolver(schema),
});
const onSubmit = async (data: FormData) => {
await new Promise((resolve) => setTimeout(resolve, 1000));
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<h2>Personal Info</h2>
<input {...register('personalInfo.firstName')} />
{errors.personalInfo?.firstName && <span>{errors.personalInfo.firstName.message}</span>}
<input {...register('personalInfo.lastName')} />
<input {...register('personalInfo.email')} />
<h2>Address</h2>
<input {...register('address.street')} />
<input {...register('address.city')} />
<input {...register('address.zipCode')} />
<h2>Preferences</h2>
<label>
<input type="checkbox" {...register('preferences.newsletter')} />
Newsletter
</label>
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Submitting...' : 'Submit'}
</button>
</form>
);
}
정리 및 체크리스트
핵심 요약
- React Hook Form: 고성능 폼 라이브러리
- 빠른 성능: 최소 리렌더링
- Validation: 내장 검증
- Zod 통합: 타입 안전성
- Controller: 커스텀 컴포넌트
- 배열 필드: useFieldArray
구현 체크리스트
- React Hook Form 설치
- 기본 폼 구현
- Validation 추가
- Zod 통합
- Controller 사용
- 배열 필드 구현
- 에러 처리
같이 보면 좋은 글
이 글에서 다루는 키워드
React Hook Form, Form, Validation, Zod, React, TypeScript, Frontend
자주 묻는 질문 (FAQ)
Q. Formik과 비교하면 어떤가요?
A. React Hook Form이 훨씬 빠르고 가볍습니다. Formik은 더 많은 기능을 제공하지만 느립니다.
Q. shadcn/ui와 함께 사용할 수 있나요?
A. 네, shadcn/ui의 Form 컴포넌트는 React Hook Form 기반입니다.
Q. 성능은 어떤가요?
A. 좋습니다. Uncontrolled input으로 리렌더링을 최소화합니다.
Q. 프로덕션에서 사용해도 되나요?
A. 네, 수많은 기업에서 안정적으로 사용하고 있습니다.