React에서 Form을 효율적으로 관리하기 위한 라이브러리이다. 이 라이브러리는 폼 상태 관리와 유효성 검사를 간편하게 처리할 수 있도록 설계되었으며, 비제어 컴포넌트(uncontrolled components)
를 기반으로 성능을 최적화한다. 특히, 다중 필드를 가진 대규모 폼이나 고성능이 요구되는 상황에서 React-Hook-Form은 매우 유용하다.
주요기능 및 특징
성능 최적화 :
react-hook-form
은 폼 요소를 직접 관리하는 ref를 활용하여, 필드 값이 변경될 때 불필요한 리렌더링을 방지한다. 이를 통해 폼의 성능을 최적화하여, 대규모 폼에서도 성능 저하 없이 동작한다.비제어 컴포넌트 기반 설계 : 라이브러리는 비제어 컴포넌트를 사용하여, 기본적인 폼의 상태를 DOM에 저장하고 필요한 시점에만 상태를 가져온다. 이는 컨트롤된 컴포넌트보다 코드가 간결해지고 성능이 향상되는 장점이 있다.
간편한 유효성 검사 :
react-hook-form
은 HTML5 기본 유효성 검사와 커스텀 유효성 검사를 함께 지원한다. 또한,Yup이나 Zod
와 같은 유효성 검사 라이브러리와도 쉽게 통합할 수 있어 복잡한 검증 로직도 간편하게 구현할 수 있다.작고 가벼운 라이브러리 :
react-hook-form
은 의존성이 거의 없고 크기가 작아, 프로젝트에 추가해도 번들 크기 증가가 적다.
export default function GraphqlMutationPage() {
const {register , handleSubmit} = useForm()
const onClickSubmit = (data)=>{
console.log(data)
}
return (
<form onSubmit={handleSubmit(onClickSubmit)}>
작성자: <input type="text" {...register("writer")} /><br />
제목: <input type="text" {...register("title")} /><br />
내용: <input type="text" {...register("contents")} /><br />
주소: <input type="text" {...register("boardAddress.addressDetail")} /><br />
<button type="submit">클릭</button>
</form>
);
}
register : state를 등록하는데 필요한 모든 기능이 들어있다.
handleSubmit : register에 적힌 state를 등록해주는 함수.
formState : 폼 상태를 포함하며,errors, isDirty, isSubmitting
과 같은 속성을 제공한다. 각 속성은 폼의 상태를 나타내며 유효성 검사와 에러 처리를 쉽게 해준다.
reset : 폼의 상태를 초기화하거나 특정 값을 설정하여 리셋할 수 있다.
React Hook Form의 기본 훅으로, 폼 상태와 메서드들을 제공한다.
defaultValues - form 초기값 설정 / API를 통해 데이터를 초기값으로 설정하거나 폼 초기 상태를 기본값으로 설정하고 싶을 때 사용한다.
mode - 유효성 검사가 실행되는 타이밍을 설정한다.onSubmit / onChange / onBlur / all
, 실시간으로 유효성 검사를 해야하는 경우onChange
를 사용하고 form 성능이 중요하거나 간단한 유효성 검사를 할 때는onSubmit
을 사용한다.
resolver - Zod, Yup 등과 같은 유효성 라이브러리와 통합할 때 사용한다. 주로 복잡한 유효성 검사를 처리하거나 데이터 스키마와 타입 정의를 함께 사용하고 싶을 때 사용한다.
shouldFocusError - 유효성 검사 실패 시, 오류가 발생한 필드로 포커스를 이동할지 여부를 설정한다. default 값으로 true이며 주로 다중 필드 폼에서 오류가 난 필드로 자동으로 포커스 이동을 원치 않는 경우 false로 설정한다.
shouldUnregister - 폼에서 필드가 UnMount되었을 때, 해당 필드를 상태에서 제거할지 여부를 설정한다. default 값으로 false이며 주로 동적으로 추가/삭제되는 필드가 많을 때, 상태를 초기화하려는 경우 true로 설정한다.
reValidateMode - 이미 유효성 검사를 받은 필드가 다시 검사를 받을 조건을 설정한다.onChange, onBlur
, 입력값 수정 중에는 검사를 피하고 싶을 때 onBlur를 사용한다.
defaultValuesFromResolver - resolve에서 가져온 값을 초기값으로 설정할지 여부를 지정한다. default 값으로 false이며 외부 유효성 검사 라이브러리에서 초기값을 자동으로 가져오고 싶을 때 true로 설정한다
criteriaMode - 에러 메세지를 수집하는 방식 설정으로 default로는 firstError이지만 모든 에러를 반환하고 싶으면 all 설정하면 된다.
delayError - 에러 메시지를 일정 시간 지연 후 표시한다. 주로 에러 메세지가 실시간으로 나타나 사용자를 방해하지 않도록 조정할 때 사용한다.
shouldUseNativeValidation - 브라우저의 기본 HTML5 유효성 검사를 활성화할 때 사용한다. default로 false이며 기본 유효성 검사를 활용해 간단한 유효성 검사를 구현할 때 사용한다.
import React from "react";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
const schema = z.object({
username: z.string().min(3, "Username must be at least 3 characters long"),
email: z.string().email("Invalid email address"),
password: z.string().min(6, "Password must be at least 6 characters long"),
});
const FormExample = () => {
const {
register,
handleSubmit,
formState: { errors },
watch,
setValue,
} = useForm({
mode: "onChange", // 실시간 유효성 검사
reValidateMode: "onBlur", // 필드 수정 후 blur 시 재검사
defaultValues: {
username: "defaultUser", // 초기값 설정
email: "",
password: "",
},
resolver: zodResolver(schema), // Zod를 사용한 유효성 검사
shouldFocusError: true, // 에러가 발생한 필드로 포커스 이동
});
const onSubmit = (data) => {
console.log("Submitted Data:", data);
};
const username = watch("username"); // 특정 필드 관찰
const setDefaultEmail = () => setValue("email", "default@example.com"); // 필드값 설정
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<label>Username</label>
<input {...register("username")} />
{errors.username && <p>{errors.username.message}</p>}
<p>Currently Typed: {username}</p>
</div>
<div>
<label>Email</label>
<input type="email" {...register("email")} />
{errors.email && <p>{errors.email.message}</p>}
<button type="button" onClick={setDefaultEmail}>
Set Default Email
</button>
</div>
<div>
<label>Password</label>
<input type="password" {...register("password")} />
{errors.password && <p>{errors.password.message}</p>}
</div>
<button type="submit">Submit</button>
</form>
);
};
export default FormExample;
Form Fleid를 React Hook Form에 등록할 때 사용한다.
register(name: string, options?: {
required?: boolean | string;
min?: number | { value: number, message: string };
max?: number | { value: number, message: string };
maxLength?: number | { value: number, message: string };
minLength?: number | { value: number, message: string };
pattern?: RegExp | { value: RegExp, message: string };
validate?: Validate | Record<string, Validate>;
valueAsNumber?: boolean;
valueAsDate?: boolean;
setValueAs?: (value: any) => any;
disabled?: boolean;
})
<input {...register("username", { required: "Username is required" })} />
Form Data를 처리하는 함수로 제출 시 유효성 검사를 실행하고 데이터 전달하는 역할을 한다.
const onSubmit = (data) => console.log(data);
<form onSubmit={handleSubmit(onSubmit)}>
<button type="submit">Submit</button>
</form>
FormState를 제공하며 유효성 검사와 관련된 여러 속성을 포함한다. 주요 속성으로 errors(필드별 에러 정보를 담고 있음) / isSubmitting(제출 중 여부) / isDirty(폼 값이 변경되었는지 여부) / isValid(모든 유효성 검사가 통과했는지 여부)
const { formState: { errors, isSubmitting, isValid } } = useForm();
특정 필드 값이나 전체 폼 데이터를 실시간으로 관찰한다.
const watchedValue = watch("username");
console.log(watchedValue);
폼 상태를 초기화하거나 특정 값을 설정하여 리셋할 수 있다.
const { reset } = useForm();
<button onClick={() => reset({ username: "default" })}>Reset</button>
특정 필드의 값을 프로그래밍 방식으로 특정 필드의 값을 설정한다.
const { setValue } = useForm();
setValue("username", "new value");
현재 폼 상태에서 특정 필드 값 또는 전체 값을 가져온다.
const { getValues } = useForm();
console.log(getValues("username")); // 특정 필드 값
console.log(getValues()); // 전체 값
특정 필드 또는 전체 폼의 에러를 제거한다.
const { clearErrors } = useForm();
clearErrors("username");
특정 필드 또는 전체 폼에 대해 유효성 검사를 수동으로 실행한다.
const { trigger } = useForm();
await trigger("email");
필드에 수동으로 에러를 설정한다
const { setError } = useForm();
setError("username", { type: "manual", message: "Custom error" });
Zod는 타입스크립트와 함께 유용하게 사용할 수 있는 스키마 기반 유효성 검사 라이브러리다. 주어진 스키마에 맞춰 데이터의 구조와 유효성을 확인할 수 있다. Zod는 타입 정의뿐만 아니라 데이터 유효성 검사를 한 번에 처리해주기 때문에, 폼 데이터나 API 요청
에서 데이터를 다룰 때 안전하게 사용할 수 있다.
Zod의 장점
타입 안전성 : TypeScript와 완벽히 통합되어, 런타임과 컴파일 타임 모두에서 타입을 강제한다.간결하고 직관적인 API : 데이터 스키마와 유효성 검사 코드를 간결하게 작성할 수 있어 가독성이 뛰어난다.
유연한 에러 처리 및 커스터마이징 : 유효성 검사 실패 시 사용자 정의 에러 메시지를 제공할 수 있다.
다양한 데이터 구조 지원 : 단순한 문자열, 숫자뿐 아니라 객체, 배열, 중첩된 구조 등 복잡한 데이터 구조의 유효성 검사도 가능하다.
라이브러리 | 주요 특징 | 장점 | 단점 |
---|---|---|---|
Zod | 스키마 기반 유효성 검사 라이브러리. TypeScript와 완벽히 통합. | - TypeScript 타입 정의와 동시 사용 - 직관적 API - 런타임 타입 안전성 제공 | - 다른 라이브러리에 비해 상대적으로 간단한 기능 |
Yup | 선언형 스키마 정의와 유효성 검사 제공. 포괄적인 데이터 유형 지원. | - 쉬운 사용법 - 널리 알려진 라이브러리 - 친숙한 API | - TypeScript와의 통합이 제한적 |
Joi | 강력하고 유연한 스키마 정의를 지원. Node.js 환경에서 자주 사용. | - 정교한 유효성 검사 가능 - 정규식, 조건부 검증 등 고급 기능 지원 | - 브라우저 환경에서 사용하려면 번들 크기가 큼 |
Zod는 z.object, z.string, z.number
등을 사용해 다양한 필드와 데이터 구조를 정의할 수 있다.
문자열 필드 유효성 검사
min( ): 문자열 최소 길이 설정
max( ): 문자열 최대 길이 설정
email( ): 이메일 형식 검사숫자 필드 유효성 검사
min( ): 최소 숫자 값을 설정
max( ): 최대 숫자 값을 설정
positive( ): 양수인지 검사
int( ): 정수인지 검사배열과 객체의 유효성 검사
배열 요소에 대한 유효성 검사 z.array( )도 가능하며, 중첩된 객체 z.object( )도 쉽게 설정할 수 있다.
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
const schema = z.object({
username: z.string().min(3, "Username must be at least 3 characters long"),
email: z.string().email("Invalid email address"),
});
const MyForm = () => {
const { register, handleSubmit, formState: { errors, isValid } } = useForm({
resolver: zodResolver(schema),
});
const onSubmit = data => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("username")} />
{errors.username && <p>{errors.username.message}</p>}
<input {...register("email")} />
{errors.email && <p>{errors.email.message}</p>}
<button type="submit" disabled={!isValid}>Submit</button>
</form>
);
};
useForm을 선언할 때 options로 resolver를 Zod schema를 넣으면 submit이 될 때 자동으로 유효성 검사가 이뤄진다.