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을 쓰기로 했다.

React Hooks 도입 후 useEffect 의존성 배열 관리