React Hook Form의 Controller를 감싸서 반복되는 설정을 줄이고 재사용성을 높이는 ControllerWrapper 컴포넌트를 소개합니다. 이 컴포넌트를 사용하면 다양한 입력 필드를 통합적으로 관리하고 유효성 검사도 쉽게 적용할 수 있습니다.
1.1. ControllerWrapper란?
ControllerWrapper는 react-hook-form의 Controller를 감싸서 보다 편리하게 사용할 수 있도록 만들어진 재사용 가능한 Form 컨트롤러 컴포넌트입니다.
🔹 주요 기능
- Controller의 공통 설정을 한 곳에 모아 관리
- 다양한 입력 필드(input, select, textarea, DatePicker 등) 지원
- rules를 활용한 유효성 검사 적용 가능
- React.forwardRef를 통해 외부에서 ref 사용 가능
1.2. ControllerWrapper 코드 구현
아래는 ControllerWrapper의 코드입니다.
// components/ControllerWrapper.tsx
import React from 'react'
import {
Control,
Controller,
FieldError,
FieldValues,
Path,
RegisterOptions,
} from 'react-hook-form'
interface ControllerWrapperProps<T extends FieldValues> {
name: Path<T> // name 타입 지정
control: Control<T> // react-hook-form의 control 객체
rules?: Omit<RegisterOptions<T, Path<T>>, 'setValueAs' | 'disabled' | 'valueAsNumber' | 'valueAsDate'>
defaultValue?: any
render: (props: RenderProps) => React.ReactElement
type?: string
[key: string]: any // 추가적인 나머지 props를 허용
}
export interface RenderProps {
value: any
onChange: (value: any) => void
ref: React.Ref<any>
error?: FieldError // 유효성 검사 오류 메시지
}
const ControllerWrapper = <T extends FieldValues>(
{
name,
control,
defaultValue,
render,
rules,
...rest
}: ControllerWrapperProps<T>,
ref: React.Ref<any>,
) => {
return (
<Controllername={name}
control={control}
defaultValue={defaultValue}
rules={rules}
render={({ field, fieldState: { error } }) => {
return render({
...field,
ref: field.ref,
error,
...rest,
})
}}
/>
)
}
export default ControllerWrapper
1.3. 사용 방법
1.3.1. useForm을 사용하여 control 가져오기
react-hook-form의 useForm()을 사용하여 control 객체를 가져옵니다.
import { useForm } from 'react-hook-form'
import ControllerWrapper from '@/components/ControllerWrapper'
type FormValues = {
username: string
age: number
}
export default function MyForm() {
const { control, handleSubmit } = useForm<FormValues>()
const onSubmit = (data: FormValues) => {
console.log('폼 데이터:', data)
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
{/* 텍스트 입력 필드 */}
<ControllerWrappername="username"
control={control}
defaultValue=""
rules={{ required: '이름을 입력해주세요' }}
render={({ value, onChange, error }) => (
<div>
<inputtype="text"
value={value}
onChange={onChange}
placeholder="이름을 입력하세요"
className="border p-2"
/>
{error && <p className="text-red-500">{error.message}</p>}
</div>
)}
/>
{/* 숫자 입력 필드 */}
<ControllerWrappername="age"
control={control}
defaultValue={0}
rules={{ required: '나이를 입력해주세요', min: { value: 1, message: '나이는 1 이상이어야 합니다' } }}
render={({ value, onChange, error }) => (
<div>
<inputtype="number"
value={value}
onChange={(e) => onChange(Number(e.target.value))}
placeholder="나이를 입력하세요"
className="border p-2"
/>
{error && <p className="text-red-500">{error.message}</p>}
</div>
)}
/>
<button type="submit" className="bg-blue-500 text-white p-2 mt-4">
제출하기
</button>
</form>
)
}
1.4. 주요 동작 원리
1️⃣ useForm()을 사용하여 control 객체를 가져옵니다.
2️⃣ ControllerWrapper를 사용하여 name, control, defaultValue, rules를 설정합니다.
3️⃣ render 함수를 통해 커스텀 입력 필드를 구성합니다.
4️⃣ onChange를 활용하여 입력 값이 변경될 때 React Hook Form에 자동으로 반영됩니다.
5️⃣ 제출 버튼 클릭 시 handleSubmit을 통해 onSubmit 함수가 실행됩니다.
1.5. 왜 ControllerWrapper를 사용할까?
✅ 1.5.1. 반복되는 Controller 사용을 간소화
매번 Controller를 직접 사용하면 코드가 길어지지만, ControllerWrapper를 활용하면 재사용 가능하고 유지보수가 쉬워집니다.
✅ 1.5.2. 유효성 검사 적용이 쉬움
rules 속성을 사용하여 필드마다 개별적인 유효성 검사를 손쉽게 추가할 수 있습니다.
✅ 1.5.3. 다양한 입력 컴포넌트에 적용 가능
render를 활용하면 input, select, textarea, DatePicker 등 어떤 입력 필드에도 적용할 수 있습니다.
1.6. 결론 🚀
✅ ControllerWrapper는 react-hook-form의 Controller를 감싸서 입력 필드의 상태 관리, 검증 및 UI 렌더링을 쉽게 할 수 있도록 도와주는 컴포넌트입니다.
✅ render 함수를 사용하여 커스텀 UI를 적용할 수 있어 다양한 입력 필드를 통합적으로 관리할 수 있습니다.
✅ rules를 통해 유효성 검사도 간편하게 추가할 수 있어 유지보수하기 쉬운 코드 구조를 만듭니다.
💡 이제 ControllerWrapper를 활용하여 보다 효율적인 Form 컴포넌트를 만들어 보세요! 🚀
'Dev > Nextjs' 카테고리의 다른 글
[Nextjs] 필터 UI → FastAPI 다중 필터 API 연결 가이드 (0) | 2025.04.01 |
---|---|
[Datepicker] useState의 비동기적 특성으로 상태 업데이트 지연 이슈 (2) | 2024.10.11 |
[React-hook-form] controller-checkbox 반복문을 사용한 방식과 개별 작성 방식의 차이 (0) | 2024.10.10 |
[Nextjs]useMemo 를 이용하여 불필요한 리렌더링 문제 해결 (0) | 2024.10.05 |
🚨[Nextjs] TypeORM 순환참조 Error - TypeORM N+1 - Lazy Loading (0) | 2024.10.01 |