useToggle의 성공에 고무된 개발팀은, 더 실용적인 문제로 눈을 돌렸다. 웹 개발에서 가장 흔하게 마주치는 작업, 바로 사용자로부터 텍스트 입력을 받는 ‘폼(Form)’이었다.
하나의 <input> 필드를 제어하기 위해, 개발자들은 언제나 비슷한 코드를 반복해서 작성해야 했다.
function UserForm() {
const [name, setName] = useState('');
const handleNameChange = (event) => {
setName(event.target.value);
};
return (
<form>
<label>Name:</label>
<input type="text" value={name} onChange={handleNameChange} />
{/* ... email, password 등 다른 필드들 ... */}
</form>
);
}
useState로 입력 값의 상태를 관리한다.onChange이벤트를 처리하는 핸들러 함수를 만든다.<input>요소에value와onChange를 연결한다.
이 세 단계의 코드는 입력 필드가 하나 추가될 때마다 거의 똑같이 반복되었다. 폼이 복잡해질수록, 컴포넌트는 수많은 상태와 핸들러 함수로 비대해졌다.
“이 반복적인 패턴 역시 커스텀 훅으로 만들 수 있습니다.”
한 개발자가 useToggle에서 얻은 아이디어를 바탕으로 새로운 훅을 제안했다.
그는 이 훅의 이름을 useInput이라고 지었다.
useInput은 입력 필드의 초기값을 인자로 받고, 폼 입력을 처리하는 데 필요한 모든 것을 객체 형태로 반환하도록 설계되었다.
import { useState } from 'react';
function useInput(initialValue = '') {
// 1. 입력 값을 위한 상태
const [value, setValue] = useState(initialValue);
// 2. onChange 핸들러
const handleChange = (event) => {
setValue(event.target.value);
};
// 3. 상태 값과 핸들러를 객체로 묶어 반환
return {
value,
onChange: handleChange,
};
}
이 useInput 훅을 사용하면, UserForm 컴포넌트는 놀랍도록 깨끗해졌다.
function UserForm() {
// useInput 훅으로 name 필드의 로직을 가져온다.
const nameInput = useInput('');
const emailInput = useInput('');
return (
<form>
<label>Name:</label>
{/* 자바스크립트의 전개 구문(...)을 사용하여 props를 간결하게 전달한다. */}
<input type="text" {...nameInput} />
<label>Email:</label>
<input type="email" {...emailInput} />
</form>
);
}
const nameInput = useInput('');
이 한 줄의 코드는 useState 선언과 onChange 핸들러 정의를 모두 대체했다.
{...nameInput}이라는 전개 구문은 value={nameInput.value}와 onChange={nameInput.onChange}를 한 번에 전달하는, 간결하고 우아한 방식이었다.
개발자들은 더 이상 모든 입력 필드마다 상태와 핸들러를 별도로 선언하고 관리할 필요가 없었다. 폼 입력 처리와 관련된 모든 보일러플레이트 코드가 useInput이라는 재사용 가능한 함수 안으로 사라진 것이다.
물론, 이 useInput은 가장 기본적인 형태였다.
팀원들은 여기에 더 많은 기능을 추가할 수 있음을 금방 알아차렸다.
“입력 값에 대한 유효성 검사(validation) 로직도 추가할 수 있겠네요.”
“onBlur 이벤트를 처리해서, 포커스가 벗어났을 때 에러 메시지를 보여주는 기능도요.”
커스텀 훅의 진정한 힘은 ‘조합’과 ‘확장’에 있었다.
개발자들은 useInput과 같은 작은 훅을 만들고, 그것들을 조합하여 useForm이라는 더 크고 복잡한 훅을 만들 수 있었다.
커스텀 훅은 리액트 개발자들에게 새로운 종류의 ‘레고 블록’을 제공했다.
상태 로직이라는 이름의 작은 블록들을 자유롭게 조립하고, 자신만의 독창적인 부품을 만들어 공유하는 문화가 싹트기 시작했다.
팀은 이제, 단순한 상태와 이벤트를 넘어, useEffect가 포함된, 즉 생명주기를 품고 있는 더 복잡한 로직을 커스텀 훅으로 추출하는 도전에 나섰다. 그들의 다음 목표는, 모든 반응형 웹사이트의 숙명과도 같은 ‘창 크기 감지’ 로직이었다.


