Will find a way
React-Hook-Form(RHF) : Form을 편하게 관리해보자 (Feat. Zod) 본문
들어가기 전
프로젝트를 진행하면서 프론트를 같이 담당하고 있는 팀원이 새로운 라이브러리를 써보자. 라는 의견을 제시해서 나온 것중에 하나가 저번 글에 작성했던 Zod였고 다른 하나는 React-hook-form 이다. 인터넷을 검색하다 보면 Zod와 React-hook-form을 같이 쓰는 글들을 찾아 보기 쉽다. 그 만큼 이 두 라이브러리 조합이 좋다는 것을 의미한다고 생각한다. 본론으로 들어가서 React-hook-form에 대해서 알아보자.
React-Hook-Form (RHF) 란?
이름에서 알 수 있듯이 form을 관리하는 라이브러리 중 하나다.
유효성 검사를 쉽게 할 수 있는, 성능이 우수하고 유연하며 확장 가능한 form을 제공하는 라이브러리이다.
줄여서 RHF라고도 하는 듯하다.
RHF 를 사용하면 뭐가 좋을까?
1) 다른 form 관리 라이브러리보다 속도가 빠르다.
2) 코드가 깔끔하다. (코드의 양을 줄여준다.)
3) 불필요한 리렌더링을 막는다. (비제어 컴포넌트를 사용해서 가능하다. 단 필요시 제어 컴포넌트도 사용하긴한다.)
비제어 컴포넌트를 사용하여 불필요한 리렌더링을 막아주는데 비제어 컴포넌트란 뭘까?
비제어가 있다면 제어도 있을것이다. 둘의 차이를 알아보자.
리액트 공식문서에도 나와있지만 한번에 이해하기 어려워서 어느 정도 적당한 비유를 찾았다.
제어(Controlled) 컴포넌트 vs 비제어(Uncontrolled) 컴포넌트
제어 컴포넌트 : 리모컨으로 조작하는 TV / 비제어 컴포넌트 : 손으로 직접 누르는 TV
이렇게 예시를 들 수 있겠다.
1) 제어 컴포넌트 (Controlled)
상태(state)를 직접 useState로 관리하고, onChange 이벤트로 제어하지 않는다.
2) 비제어 컴포넌트 (Unontrolled)
내부에서 알아서 작동, React 값을 직접 제어하지 않는다.
방식 | 설명 | 코드 특징 |
제어 컴포넌트(Controlled) | React state로 값을 직접 관리 | useState + value, onChange |
비제어 컴포넌트(Uncontrolled) | DOM 자체가 값을 관리하고 React는 참조만 담당 | defaultValue + ref |
코드 예시
제어 방식 (Controlled)
const [email, setEmail] = useState('');
<input
type="text"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
상태를 직접 관리하고 있기 때문에 리렌더가 많아지고 성능 이슈가 생길 수 있다.
비제어 방식 (Uncontrolled)
const inputRef = useRef(null);
<input type="text" ref={inputRef} defaultValue="기본값" />
React는 상태를 모르기 때문에 DOM에서 직접 값을 꺼내서 사용한다.
간단하게 제어 컴포넌트와 비제어 컴포넌트에 대해서 알아봤다. 다시 RHF로 돌아가보자.
RHF jsx코드로 기능 알아보기
const {
register,
handleSubmit,
formState: { errors },
} = useForm();
const onSubmit = (data) => {
console.log(data);
};
const onValid = (errors) => {
console.log(errors);
};
return (
<form onSumbmit={handleSubmit(onSubmit, onValid)}>
<input {...register('email', { required: '이메일을 적어주세요.' })}/>
{errors.email && <span>{errors.email.message}</span>}
<button type="submit">제출</button>
</form>
);
register()의 역할
1) ref 연결 (비제어 방식)
2) onChange, onBlur, name, value 자동 설정
...register 를 스프레드 연산자로 쓰는 이유
register는 필드가 RHF에 연결되도록 모든 필수 props를 넘겨준다.
스프레드 연산자를 쓰지 않는다면 아래 코드처럼 길어진다. (한마디로 간편하게 쓰기 위함)
<input
name="email"
ref={ref}
onChange={onChange}
onBlur={onBlur}
/>
유효성 검증 옵션 예시
register('register', {
required: '비밀번호를 입력해주세요.',
minLength: { value: 8, message: '8자 이상 입력해주세요. '},
});
RHF 필수 메서드 / 기능 정리
메서드 | 설명 | 예시 |
register() | input 연결 + 유효성 옵션 설정 | register('email, { required: true }) |
handleSubmit() | 첫번째 매개변수 : 제출시 실행할 함수 감싸줌, 두번쨰 매개변수 : 에러시 실행될 함수를 감싸줌 |
onSubmit={handleSubmit(fn1, fn2)} |
formState.errors | 각 필드의 에러 메시지 확인 | errros.email?.message |
watch() | 특정 필드 값 실시간 추적 | watch('password') |
getValues() | 전체/부분 필드 값 가져옴 | getValues('nickname') |
reset() | 폼 초기화 | reset({ email: '', nickname: '' }) |
setValue() | 특정 값 직접 사용 | setValue('email', 'jaka@story.com') |
trigger() | 유효성 수동 검사 | trigger('password') |
control | Controller에서 사용 | <Controller control={control} ... /> |
Zod 등 스키마 기반 유효성과 통합
const schema = z.object({ email: z.string().email() });
const {
register,
handleSubmit,
formState: { errors },
} = useForm({ resolver: zodResolver(schema) });
zod와 너무 조합이 좋아서 조금만 사용해도 정말 좋다는 것을 경험할수 있을 것이다.
마무리 (잡담)
RHF에 대해서도 포스팅을 마무리를 하려고 한다. 블로그를 써보니 내용을 간단하게 쓰려고 하지만 생각보다 작성하는 시간이 너무 오래 걸린다. 아직 나의 깊이가 이 정도 밖에 안되서 어느 정도 사용법만 익히고 있는 느낌이 들기도 하다. 그래도 할 것들이 많으니 오늘은 여기까지만 작성하고 또 프로젝트를 하러 가야지. 많은 경험을 해야 많은 것들을 사용할 수 있으니 말이다.
'FrontEnd > React' 카테고리의 다른 글
상태관리 : 약관동의 구현 (모든 약관, 필수약관) / TypeScript, JS 메서드 (0) | 2025.05.06 |
---|---|
새로운 라우터가 등장했다고? (createBrowserRouter) (0) | 2025.04.10 |
[React] Zustand 에 대해서 (React 상태관리) (0) | 2025.04.01 |
[React] Vite에 대해서 (1) | 2025.01.29 |
[React] Redux / Reducer를 이용한 예제 (1) | 2024.10.22 |