react-hook-form을 어떻게 쓰는지, 내가 만든 인풋이랑 연결은 어떻게 하는지
유효성 검사를 라이브러리 써서 어떻게 하는지..
차근차근 공부 스타투,,
1단계: Hook Form ?
React Hook Form은 리액트에서 폼을 쉽게 관리하고, 유효성 검사까지 도와주는 도구
쉽게 말하면, 입력창 값들을 알아서 관리해주고 검사해주는 라이브러리
2단계: 왜 필요한데?
리액트에서 <input>을 많이 쓰잖아. 그런데 각각의 <input>을 useState로 관리하면 코드가 많아져.
Hook Form을 쓰면 그걸 줄일 수 있어.
const [email, setEmail] = useState('');
<input value={email} onChange={(e) => setEmail(e.target.value)} />
이런 걸 생략하고, Hook Form한테 맡길 수 있어.
3단계: register가 뭐야?
Hook Form에서는 register()라는 함수를 써서 "이 인풋을 Hook Form이 관리해줘!"라고 등록하는 거야.
<input {...register("email")} />
- "email"은 변수 이름 같은 거야. Hook Form이 이 이름으로 값을 기억해.
- ...register()는 input에 여러 속성을 자동으로 넣어주는 거야. (onChange, onBlur, ref 등등)
4단계: 왜 ... 스프레드 문법이야?
...register("email") 이렇게 쓰는 이유는,
register() 함수가 다음처럼 여러 개의 props를 리턴하기 때문이야:
register("email") ➡️ {
onChange: ...,
onBlur: ...,
name: "email",
ref: ...
}
그래서 이렇게 바로 input에 붙이는 거야:
<input {...register("email")} />
스프레드 문법(...)은 이 여러 props를 input에 한 번에 넣는 역할을 해!
5단계: 그럼 이걸 쓰면 뭐가 좋아?
- input 값 관리를 따로 안 해도 돼 (useState 안 써도 됨)
- 유효성 검사도 간단히 붙일 수 있어
- form 전체의 데이터도 쉽게 한 번에 가져올 수 있어
CASE 1. 인풋 컴포넌트 분리했을 때
1. ref ?
ref는 HTML 요소(예: <input>)에 직접 접근하고 싶을 때 사용하는 React 기능이다
예를 들어 input 태그에 커서를 자동으로 옮기고 싶다거나, 값을 직접 읽고 싶을 때 쓴다.
근데 지금 상황에서는 react-hook-form이 input을 제어하려고 ref를 필요로 함.
쉽게 말해:
→ react-hook-form이 "Input을 감지" 하려면 ref를 통해 그 input DOM을 직접 연결해야 해!
ref를 안쓰면, input에 접근이 안되는 건가 ?
▶︎ 일반적인 React에서는
ref를 직접 쓸 일이 많지는 않다. 대부분 상태(useState)나 props로 컨트롤함.
const [value, setValue] = useState('');
<input value={value} onChange={(e) => setValue(e.target.value)} />
이렇게 쓰면 ref 없이도 잘 동작하지.
ref는 뭔가 그 인풋과 훅폼을 연결해주는 거라고 생각~
▶︎ 그런데 react-hook-form에서는?
react-hook-form은 다음 두 가지 방식 중 하나로 input을 추적할 수 있다
1. ref로 직접 DOM 연결 (기본 방식)
<input {...register('email')} />
이때 register가 내부적으로 ref를 연결해서 form 상태를 추적해.
이 방식은 빠르고 가볍고, 코드가 짧다 → 가장 많이 씀
2. Controller라는 컴포넌트를 사용 (ref 안 쓸 수 있음)
import { Controller } from 'react-hook-form';
<Controller
name="email"
control={control}
render={({ field }) => (
<Input
value={field.value}
onChange={field.onChange}
/>
)}
/>
이 방식은 ref 없이도 가능하지만…
- 코드가 좀 복잡해지고
- react-hook-form이 자동 추적하는 장점이 줄어듬
2. react-hook-form을 쓰는 방법
설치 먼저 하기
npm install react-hook-form
혹은 yarn 쓰면:
yarn add react-hook-form
예제 구조
목표: 내가 만든 공통 Input 컴포넌트 그대로 사용하면서 react-hook-form 적용해보기!
3. 전체 예제
Input.tsx (공통 인풋 컴포넌트)
import React, { forwardRef } from 'react';
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
error?: string;
}
// ref를 받아야 react-hook-form이 이 인풋을 인식함
const Input = forwardRef<HTMLInputElement, InputProps>(
({ error, ...props }, ref) => {
return (
<div className="flex flex-col gap-1">
<input
ref={ref} // 이게 핵심!
{...props}
className={`border px-3 py-2 rounded ${
error ? 'border-red-500' : 'border-gray-300'
}`}
/>
{error && <span className="text-red-500 text-sm">{error}</span>}
</div>
);
}
);
export default Input;
Register.tsx (회원가입 페이지)
import { useForm } from 'react-hook-form';
import Input from '@/components/common/Input';
type FormValues = {
email: string;
password: string;
};
export default function Register() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<FormValues>();
const onSubmit = (data: FormValues) => {
console.log('회원가입 완료!', data);
};
return (
<form onSubmit={handleSubmit(onSubmit)} className="flex flex-col gap-4 w-[300px] mx-auto">
<Input
placeholder="이메일"
{...register('email', {
required: '이메일은 필수입니다',
pattern: {
value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
message: '이메일 형식이 아니에요',
},
})}
error={errors.email?.message}
/>
<Input
placeholder="비밀번호"
type="password"
{...register('password', {
required: '비밀번호는 필수입니다',
minLength: {
value: 6,
message: '6자 이상이어야 해요',
},
})}
error={errors.password?.message}
/>
<button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded">
회원가입
</button>
</form>
);
}
요약
ref | react-hook-form이 input을 추적하기 위해 사용함 (input의 실제 DOM 연결) |
forwardRef | 내가 만든 커스텀 Input에 ref 전달 가능하게 만드는 React 기능 |
react-hook-form | 폼을 쉽게 만들고 유효성 검사해주는 인기 라이브러리 |
*fowardRef ?
forwardRef는 뭐야?
React에서 기본적으로는 부모가 자식 컴포넌트 안에 있는 DOM 요소(input)에 직접 접근할 수 없어.
그래서 부모가 준 ref를 자식이 직접 input에 달아줘야 해.
👉 이걸 가능하게 해주는 게 바로 **forwardRef (ref를 전달해주는 함수)**야!
❌ forwardRef 없이 쓴 경우
function Input(props) {
return <input {...props} />;
}
// 부모에서 사용
<Input {...register("email")} />
이렇게 하면 register() 안에 있는 ref가 <input>까지 도달 못 해.
=> ❗ React Hook Form이 제대로 동작하지 않아.
forwardRef를 쓰면
import React from "react";
// 자식 컴포넌트
const Input = React.forwardRef((props, ref) => {
return <input ref={ref} {...props} />;
});
- 여기서 forwardRef가 ref를 받아서 내부 <input>에 직접 연결해주는 역할을 해.
- 이러면 부모에서 넘긴 register("email")이 자식의 <input>까지 연결돼!
쉽게 요약하면
용어 설명ref | React Hook Form이 input을 "잡기" 위한 손잡이 |
공통 컴포넌트 | input이 자식 안에 있어서 Hook Form이 못 만짐 |
forwardRef | 부모가 준 ref를 자식의 <input>에 직접 연결해주는 다리 역할 |
// 공통 Input.tsx
import React from "react";
const Input = React.forwardRef((props, ref) => {
return <input ref={ref} {...props} />;
});
export default Input;
// 부모 컴포넌트에서
import { useForm } from "react-hook-form";
import Input from "./Input";
function MyForm() {
const { register, handleSubmit } = useForm();
return (
<form onSubmit={handleSubmit(data => console.log(data))}>
<Input {...register("email")} />
<button type="submit">제출</button>
</form>
);
}
이렇게 하면 Hook Form이 공통 Input 안의 <input>까지 연결돼서
입력값 관리, 검사, 제출 다 잘 된다.
Case 2 : 한 페이지 내에서 인풋 하드코딩 하고, 유효성 검사도 그 페이지에서 할 때
공통 인풋 컴포넌트 안 쓰고, 로그인 페이지에서 직접 input을 하드코딩
React Hook Form 쓸 때 훨~씬 쉬워져!
별다른 복잡한 ref, forwardRef, props 전달 이런 거 신경 안 써도 돼.
왜 쉬워지냐면?
공통 컴포넌트를 안 쓰면,
- input이 그 자리에 직접 있으니까
- register()로 바로 연결할 수 있고
- ref를 전달할 필요도 없어
→ Hook Form이 그냥 input을 바로 잡아서 관리할 수 있음
기존 공통 컴포넌트 방식 (복잡)
<Input {...register("email", { required: "필수" })} />
→ Input 안에서 ref 연결 해줘야 함 → 복잡…
하드코딩 방식 (간단)
<input
{...register("email", { required: "이메일을 입력해주세요" })}
/>
{errors.email && <p>{errors.email.message}</p>}
→ 이게 끝! 아주 간단함!
요약
방식 차이점
공통 컴포넌트 사용 | 재사용은 좋지만 ref 전달, forwardRef 필요함 |
하드코딩 input 사용 | 코드 반복은 좀 있지만 Hook Form 쓰기 훨씬 간단함 |
'Studying > React' 카테고리의 다른 글
리액트 공부하기 - hook form (0) | 2025.06.24 |
---|---|
리액트 - input은 문자열 (1) | 2025.06.24 |
리액트 타입스크립트 공부하기 - input 박스 관련 속성 (0) | 2025.06.23 |
리액트 타입스크립트 공부하기 - props로 테일윈드 className 속성 내려주기 (0) | 2025.06.23 |
리액트 절대경로 @ 커스텀 공부하기 (4) | 2025.06.20 |