React Hooks 도입 후 useEffect 의존성 배열 관리 경험
배경
올해 초 React Hooks가 정식 출시된 이후, 팀에서 신규 기능부터 Hooks로 작성하기 시작했다. Class 컴포넌트에 익숙했던 터라 lifecycle 메서드를 useEffect로 바꾸는 과정이 직관적이지 않았다.
문제 상황
사용자 프로필 페이지에서 특정 조건일 때만 데이터를 다시 fetch해야 하는 로직이 있었다.
function UserProfile({ userId }) {
const [profile, setProfile] = useState(null);
const [filter, setFilter] = useState('all');
useEffect(() => {
fetchUserProfile(userId, filter).then(setProfile);
}, [userId]); // filter를 의존성에서 누락
// ...
}
filter 변경 시 데이터가 갱신되지 않는 버그가 발생했다. eslint-plugin-react-hooks의 exhaustive-deps 규칙이 경고를 띄웠지만 처음엔 무시했다.
해결 과정
의존성 배열을 제대로 이해하는 게 핵심이었다. useEffect 내부에서 사용하는 모든 외부 값은 배열에 포함되어야 한다.
useEffect(() => {
fetchUserProfile(userId, filter).then(setProfile);
}, [userId, filter]); // 올바른 의존성 배열
만약 특정 값 변경 시에만 실행하고 싶다면, 로직을 분리하거나 useRef를 활용하는 방법도 있었다.
교훈
- eslint-plugin-react-hooks 규칙을 신뢰하자
- componentDidUpdate의 조건문 로직을 useEffect로 그대로 옮기면 안 된다
- 의존성 배열은 "언제 실행할지"가 아니라 "무엇을 참조하는지"를 명시하는 것
Hooks의 mental model을 제대로 이해하는 데 시간이 걸렸지만, 이제는 Class 컴포넌트보다 훨씬 직관적으로 느껴진다.