React Hooks 도입 후 Custom Hook으로 API 호출 로직 분리하기

배경

올해 초 React Hooks가 정식 출시된 이후, 팀에서 신규 기능부터 Hooks를 사용하기로 결정했다. 기존 Class Component에서 반복적으로 작성하던 componentDidMount, componentDidUpdate 로직을 useEffect로 대체하는 과정에서 API 호출 패턴이 계속 중복되는 것을 발견했다.

문제점

매번 컴포넌트마다 loading, error, data 상태를 선언하고 동일한 try-catch 패턴을 작성하고 있었다. 코드 리뷰 중 이 부분이 지적되어 Custom Hook으로 추출하기로 했다.

구현

import { useState, useEffect } from 'react';

function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    let cancelled = false;

    setLoading(true);
    fetch(url)
      .then(res => res.json())
      .then(data => {
        if (!cancelled) {
          setData(data);
          setLoading(false);
        }
      })
      .catch(err => {
        if (!cancelled) {
          setError(err);
          setLoading(false);
        }
      });

    return () => {
      cancelled = true;
    };
  }, [url]);

  return { data, loading, error };
}

사용 예시

function UserProfile({ userId }) {
  const { data, loading, error } = useFetch(`/api/users/${userId}`);

  if (loading) return <Spinner />;
  if (error) return <ErrorMessage error={error} />;
  return <Profile user={data} />;
}

개선 효과

  • 컴포넌트 코드가 30% 정도 감소
  • cleanup 로직(cancelled 플래그)을 빠뜨리는 실수 방지
  • 테스트 코드 작성이 용이해짐

다음 스프린트에서는 POST/PUT 요청을 위한 useMutation Hook도 만들 예정이다.

React Hooks 도입 후 Custom Hook으로 API 호출 로직 분리하기