React 19 RC의 use() 훅으로 데이터 페칭 패턴 개선하기

배경

프로젝트에서 데이터 페칭 로직이 항상 고민이었다. useEffect로 fetch를 호출하면 로딩 상태 관리가 번거롭고, React Query를 쓰기엔 작은 프로젝트에서 오버엔지니어링처럼 느껴질 때가 있었다.

React 19 RC에서 use() 훅이 추가되어 Promise를 직접 다룰 수 있다는 소식을 듣고 실험해봤다.

기존 패턴

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch(`/api/users/${userId}`)
      .then(res => res.json())
      .then(data => {
        setUser(data);
        setLoading(false);
      });
  }, [userId]);

  if (loading) return <Spinner />;
  return <div>{user.name}</div>;
}

use() 훅 적용

import { use, Suspense } from 'react';

function UserProfile({ userPromise }) {
  const user = use(userPromise);
  return <div>{user.name}</div>;
}

function App() {
  const userPromise = fetch('/api/users/1').then(r => r.json());
  
  return (
    <Suspense fallback={<Spinner />}>
      <UserProfile userPromise={userPromise} />
    </Suspense>
  );
}

로딩 상태를 수동으로 관리할 필요가 없어졌다. Suspense 경계에서 일괄 처리되니 여러 컴포넌트의 로딩을 조율하기도 편했다.

주의사항

  • Promise를 컴포넌트 내부에서 생성하면 리렌더 시마다 새로운 요청이 발생한다. 상위에서 생성하거나 캐싱 레이어가 필요하다.
  • 에러 처리는 Error Boundary와 함께 사용해야 한다.
  • 아직 RC 단계라 프로덕션 투입은 신중하게 결정해야 한다.

결론

간단한 데이터 페칭에는 충분히 실용적이었다. React Query의 캐싱, 리페칭 등 고급 기능이 필요 없는 경우 use() 훅만으로도 깔끔한 코드를 작성할 수 있었다. 정식 릴리스 후 실무에 점진적으로 도입해볼 계획이다.