React Hooks 도입 후 Custom Hook으로 폼 로직 재사용하기
배경
회원가입, 로그인, 설정 페이지 등 폼을 다루는 컴포넌트가 많아지면서 input 상태 관리와 validation 로직이 중복되기 시작했다. 기존에는 HOC로 처리했지만 wrapper hell과 props drilling 문제가 있었다.
React 16.8이 정식 릴리즈된 후 Hooks를 점진적으로 도입 중이었고, Custom Hook으로 이 문제를 해결할 수 있을 것 같았다.
useForm 구현
import { useState } from 'react';
function useForm(initialValues) {
const [values, setValues] = useState(initialValues);
const [errors, setErrors] = useState({});
const handleChange = (e) => {
const { name, value } = e.target;
setValues(prev => ({
...prev,
[name]: value
}));
};
const handleSubmit = (onSubmit, validate) => (e) => {
e.preventDefault();
const validationErrors = validate ? validate(values) : {};
setErrors(validationErrors);
if (Object.keys(validationErrors).length === 0) {
onSubmit(values);
}
};
return { values, errors, handleChange, handleSubmit };
}
사용 예시
function LoginForm() {
const { values, errors, handleChange, handleSubmit } = useForm({
email: '',
password: ''
});
const validate = (values) => {
const errors = {};
if (!values.email.includes('@')) {
errors.email = '유효한 이메일을 입력하세요';
}
if (values.password.length < 6) {
errors.password = '비밀번호는 6자 이상이어야 합니다';
}
return errors;
};
const onSubmit = (values) => {
console.log('로그인:', values);
};
return (
<form onSubmit={handleSubmit(onSubmit, validate)}>
<input
name="email"
value={values.email}
onChange={handleChange}
/>
{errors.email && <span>{errors.email}</span>}
<input
type="password"
name="password"
value={values.password}
onChange={handleChange}
/>
{errors.password && <span>{errors.password}</span>}
<button type="submit">로그인</button>
</form>
);
}
결과
5개 폼 컴포넌트에 적용했더니 약 200줄의 중복 코드가 제거됐다. HOC 대비 컴포넌트 구조도 훨씬 간결해졌고, 디버깅도 쉬워졌다. Hooks의 조합 가능성 덕분에 다른 Custom Hook과도 쉽게 결합할 수 있었다.
아직 팀 전체가 Hooks에 익숙하지 않아 러닝 커브는 있지만, 점진적으로 확대 적용할 계획이다.