React Hook Form
React에서 폼(form) 상태와 검증을 쉽게 관리할 수 있게 해주는 라이브러리입니다.
1
npm install react-hook-form
최소한의 리렌더링 : 입력할 때마다 전체 컴포넌트가 리렌더링되지 않음
간편한 validation : 외부 validation 라이브러리(Zod, Yup)와 쉽게 통합 가능
성능 최적화 : register로 각 input만 상태를 추적하기 때문에 불필요한 렌더링을 줄임
TypeScript 친화적 : 제네릭으로 타입 안전하게 폼 데이터를 관리 가능
useForm
1
const methods = useForm<FormDataType>(options);
useForm 주요 옵션
defaultValues (object): 폼 필드의 초기 값 설정resolver (function): 외부 validation 라이브러리(Zod, Yup 등) 연결mode: validation을 언제 실행할지를 결정, 기본값은 ‘onSubmit’- ‘onSubmit’ : 폼 제출 시 (default)
- ‘onBlur’ : input에서 focus를 잃을 때
- ‘onChange’ : 입력이 바뀔 때마다
- ‘all’ : onBlur + onChange 모두
reValidateMode: 이미 에러가 발생한 필드가 다시 validation 될 타이밍을 설정, 기본값은 ‘onSubmit’- ‘onChange’ : 에러 난 필드의 값이 바뀔 때마다 (default)
- ‘onBlur’ : 에러 난 필드가 blur 될 때만
- ‘onSubmit’ : 에러 난 필드도 submit 시에만
criteriaMode: 한 필드에서 여러 validation 규칙을 걸었을 때 어떻게 에러를 보여줄지 결정, 기본값은 ‘firstError’- ‘firstError’ : 첫 번째 에러만 보여줌 (default)
- ‘all’ : 모든 에러 메시지를 배열로 반환
shouldFocusError (boolean): 에러 발생 시, 에러가 있는 첫 번째 필드로 포커스 이동 여부를 결정shouldUnregister (boolean): 언마운트 시 필드 값 제거 여부 (default: true)
1
2
3
4
5
6
7
8
9
10
11
const { register, handleSubmit, formState } = useForm<LoginFormData>({
defaultValues: {
id: "",
password: "",
},
resolver: zodResolver(loginSchema),
mode: "onBlur", // blur 시 validation
criteriaMode: "firstError", // 첫 번째 에러만 보여줌
shouldFocus
Error: true,
});
useForm 핵심 메서드
useForm()은 주요 메서드들이 들어있는 객체를 반환합니다.
폼 관리 관련
register: input 요소를 RHF와 연결하는 메서드 (필수)첫 번째 인자: 필드 이름 (‘email’) 두 번째 인자: 유효성 검사 규칙 (required, minLength, validate 등)
1
<input {...register("email", { required: "이메일은 필수입니다." })} />
unregister: input field 등록 해제setValue(name, value): 필드 값 직접 설정getValues(): 현재 폼 값 가져오기reset(values?): 폼 초기화, 옵션으로 값 지정 가능1 2
reset(); // defaultValues로 리셋 reset({ email: "", password: "" }); // 새 값으로 리셋
watch(name?): 특정 필드 값 관찰, 실시간으로 값 가져오기
제출 관련
handleSubmit(onValid, onInvalid?): form submit 처리, validation 성공 시 onValid 호출, 실패 시 onInvalid 호출1 2 3
const onSubmit = (data: LoginFormData) => console.log(data); <form onSubmit={handleSubmit(onSubmit)} />;
trigger(name): 수동으로 유효성 검사 실행1 2
await trigger(); // 전체 필드 검사 await trigger("email"); // 특정 필드만 검사
폼 상태 관련 (formState)
formState 객체를 반환
errors: 필드별 validation 에러 객체isSubmitting: 제출 중 여부 (submit 버튼 disable 처리 가능)isSubmitSuccessful: 제출 성공 여부 (submit 버튼 disable 처리 가능)isDirty: 하나라도 폼 값이 변경되었는지 여부dirtyFields: 값이 변경된 필드 정보isValid: 폼 전체가 유효한지 여부touchedFields: focus 후 blur된 필드 정보
1
2
3
const {
formState: { errors, isSubmitting, isValid },
} = useForm();
useForm 사용 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const {
register, // input 등록
handleSubmit, // submit 처리
watch, // 값 감시
setValue, // 값 설정
getValues, // 값 가져오기
reset, // 초기화
unregister, // 등록 해제
formState: {
errors, // validation 에러
isSubmitting, // 제출 중
isDirty, // 값 변경 여부
isValid, // 유효성 여부
touchedFields, // 포커스 후 blur 된 필드
dirtyFields, // 값 변경된 필드
},
} = useForm<LoginFormData>({
defaultValues: { id: "", password: "" },
resolver: zodResolver(loginSchema),
mode: "onBlur",
shouldFocusError: true,
});
기본 예제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import { useForm } from "react-hook-form";
type FormData = {
username: string;
password: string;
};
const MyForm = () => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<FormData>();
const onSubmit = (data: FormData) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("username", { required: "아이디를 입력하세요" })} />
{errors.username && <span>{errors.username.message}</span>}
<input
type="password"
{...register("password", { required: "비밀번호를 입력하세요" })}
/>
{errors.password && <span>{errors.password.message}</span>}
<button type="submit">로그인</button>
</form>
);
};
응용 - 외부 validation 라이브러리 연결
Zod와 함께 사용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// zodResolver
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
const loginSchema = z.object({
id: z.string().min(1, "아이디를 입력하세요"),
password: z.string().min(6, "비밀번호는 6자리 이상입니다"),
});
type LoginFormData = z.infer<typeof loginSchema>;
const {
register,
handleSubmit,
formState: { errors },
} = useForm<LoginFormData>({
resolver: zodResolver(loginSchema),
});
- zodResolver가 Zod schema를 RHF에 연결
- safeParse처럼 별도로 에러를 매핑할 필요 없음
- errors 객체에 자동으로 필드별 메시지가 들어옴