2025.1.14 화요일의 공부기록
React Suspense는 React에서 비동기 작업(예: 데이터 Fetching, 컴포넌트 로드 등)을 관리하고, 작업이 완료될 때까지 대체 UI(로딩 상태 등)를 표시할 수 있도록 지원하는 기능이다. Suspense는 React 16.6에서 처음 도입되었으며, 서버 렌더링(SSR)과 클라이언트 렌더링(CSR) 모두에서 활용할 수 있다.
비동기 상태 관리
Suspense는 비동기 작업이 완료될 때까지 Fallback UI를 표시하며, 완료되면 원래 컴포넌트를 렌더링한다.
로딩 UI 처리
비동기 데이터를 기다리는 동안 스피너, 스켈레톤, "로딩 중" 메시지와 같은 로딩 상태를 간단히 표시할 수 있다.
클라이언트 및 서버 렌더링 지원
Suspense는 CSR과 SSR 모두에서 사용할 수 있으며, 서버에서는 데이터 Fetching과의 통합에 특히 유용하다.
Suspense는 React.Suspense
컴포넌트를 통해 사용할 수 있다.
fallback
속성을 이용하여 비동기 작업 중 표시할 대체 UI를 정의한다.
import React, { Suspense } from "react";
const LazyComponent = React.lazy(() => import("./MyComponent"));
export default function App() {
return (
<div>
<h1>React Suspense Example</h1>
<Suspense fallback={<p>Loading Component...</p>}>
<LazyComponent />
</Suspense>
</div>
);
}
React.lazy
사용).React 18부터 Suspense는 데이터 Fetching과 통합되어, 서버에서 데이터를 Fetch하는 동안 로딩 상태를 처리할 수 있다.
export default async function Page() {
const data = await fetchData(); // 서버에서 데이터를 Fetch
return (
<div>
<h1>{data.title}</h1>
</div>
);
}
function fetchData() {
return new Promise((resolve) =>
setTimeout(() => resolve({ title: "Hello, Suspense!" }), 2000)
);
}
import { Suspense } from "react";
import Page from "./Page";
export default function App() {
return (
<div>
<Suspense fallback={<p>Loading Page...</p>}>
<Page />
</Suspense>
</div>
);
}
Next.js에서는 Suspense와 서버 컴포넌트를 함께 사용해 서버에서 데이터를 Fetch하고, 스트리밍 방식으로 렌더링할 수 있다.
import { Suspense } from "react";
function UserProfile() {
const data = fetchUserData(); // 서버에서 Fetch
return <h1>{data.name}</h1>;
}
function fetchUserData() {
return { name: "John Doe" }; // 가짜 데이터
}
export default function Page() {
return (
<Suspense fallback={<p>Loading Profile...</p>}>
<UserProfile />
</Suspense>
);
}
로딩 상태 처리 간소화
별도의 상태 관리 없이 로딩 상태를 처리할 수 있다.
사용자 경험 향상
로딩 중에도 의미 있는 대체 UI를 제공하여 빈 화면을 방지한다.
코드 스플리팅과 통합
React.lazy
를 통해 동적으로 컴포넌트를 로드할 때 대체 UI를 표시할 수 있다.
서버와 클라이언트 렌더링 통합
데이터 Fetching과 서버 컴포넌트 렌더링에 유용하게 활용된다.
지원되는 환경
React 16.6 이상에서 사용할 수 있다. 서버와 클라이언트 모두에서 Suspense를 제대로 활용하려면 React 18 이상을 권장한다.
오직 Fallback UI만 지원
Suspense는 로딩 상태를 관리하지만, 에러 처리는 지원하지 않는다. 에러 처리를 위해서는 ErrorBoundary
를 함께 사용해야 한다.
비동기 작업의 제약
Suspense는 React 내부에서 지원하는 비동기 작업(예: React.lazy
, 서버 컴포넌트)과만 호환된다.
로딩 상태와 에러 처리를 통합하려면 Suspense와 ErrorBoundary를 함께 사용한다.
import React, { Suspense } from "react";
function ErrorFallback({ error }: { error: Error }) {
return <p>Error: {error.message}</p>;
}
function ErrorBoundary({ children }: { children: React.ReactNode }) {
return (
<React.ErrorBoundary FallbackComponent={ErrorFallback}>
{children}
</React.ErrorBoundary>
);
}
export default function App() {
return (
<ErrorBoundary>
<Suspense fallback={<p>Loading...</p>}>
<LazyComponent />
</Suspense>
</ErrorBoundary>
);
}
아래는 Suspense
를 사용해 영화 정보를 병렬로 Fetch하고, 각각 로딩 상태를 처리하는 예제이다.
import { Suspense } from "react";
import MovieInfo from "../../../components/movie-info";
import MovieVideos from "../../../components/movie-videos";
type Props = {
params: { id: string };
searchParams: { region: string; page: string };
};
export default async function MovieDetail({ params, searchParams }: Props) {
const { id } = await params;
const { region, page } = await searchParams;
return (
<div>
{/* MovieInfo 컴포넌트 로딩 처리 */}
<Suspense fallback={<h1>Loading movie info</h1>}>
<MovieInfo id={id} />
</Suspense>
{/* MovieVideos 컴포넌트 로딩 처리 */}
<Suspense fallback={<h1>Loading movie videos</h1>}>
<MovieVideos id={id} />
</Suspense>
</div>
);
}