React 18 Suspense와 데이터 페칭 패턴 정리
배경
올해 3월 React 18이 정식 출시되면서 Suspense for Data Fetching이 Concurrent Feature로 포함되었다. 기존 프로젝트에 점진적으로 적용하면서 몇 가지 패턴을 정리할 필요가 있었다.
기존 방식의 문제
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchUser(userId).then(data => {
setUser(data);
setLoading(false);
});
}, [userId]);
if (loading) return <Spinner />;
return <div>{user.name}</div>;
}
매번 loading 상태를 관리하고, 에러 처리를 각 컴포넌트에서 반복해야 했다.
Suspense 적용
function UserProfile({ userId }) {
const user = use(fetchUser(userId)); // experimental
return <div>{user.name}</div>;
}
// 상위 컴포넌트
function App() {
return (
<Suspense fallback={<Spinner />}>
<ErrorBoundary>
<UserProfile userId={123} />
</ErrorBoundary>
</Suspense>
);
}
로딩과 에러 상태를 상위에서 선언적으로 처리할 수 있게 되었다.
주의사항
아직 use hook은 experimental이라 프로덕션에서는 React Query나 SWR 같은 라이브러리의 Suspense 모드를 사용하는 게 안전하다. React Query v4에서 suspense: true 옵션으로 간단히 적용 가능했다.
const { data } = useQuery(['user', userId], () => fetchUser(userId), {
suspense: true
});
결론
Suspense는 컴포넌트 코드를 단순하게 만들어줬지만, 아직 생태계가 완전히 준비되지 않았다. 당분간은 검증된 라이브러리와 함께 사용하는 게 현실적이다.