React Hooks 도입 후 useEffect 의존성 배열 관리
문제 상황
팀 프로젝트에서 React 16.8 Hooks를 도입한 지 두 달째다. 클래스 컴포넌트를 함수형으로 전환하는 작업 중 useEffect 의존성 배열 관리에서 자주 실수가 발생했다.
특히 문제가 됐던 케이스는 useEffect 내부에서 사용하는 함수를 의존성 배열에 넣었을 때였다.
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const fetchUser = () => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => setUser(data));
};
useEffect(() => {
fetchUser();
}, [fetchUser]); // 무한 루프 발생
}
렌더링마다 fetchUser가 새로 생성되어 useEffect가 계속 실행되는 문제였다.
해결 방법
1. useCallback 사용
const fetchUser = useCallback(() => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => setUser(data));
}, [userId]);
useEffect(() => {
fetchUser();
}, [fetchUser]);
2. 함수를 useEffect 내부로 이동
더 간단한 방법은 함수를 useEffect 안으로 옮기는 것이다.
useEffect(() => {
const fetchUser = () => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => setUser(data));
};
fetchUser();
}, [userId]);
정리
useEffect 의존성 배열은 eslint-plugin-react-hooks의 exhaustive-deps 규칙을 켜두고 경고를 무시하지 않는 게 중요하다. 처음엔 귀찮지만 버그를 사전에 방지할 수 있었다.
팀 컨벤션으로는 가능하면 함수를 useEffect 내부에 두고, 여러 곳에서 재사용이 필요한 경우에만 useCallback을 쓰기로 했다.