Suspense

Odyssey·2025년 1월 14일
0

Next.js_study

목록 보기
13/58
post-thumbnail

2025.1.14 화요일의 공부기록

React Suspense는 React에서 비동기 작업(예: 데이터 Fetching, 컴포넌트 로드 등)을 관리하고, 작업이 완료될 때까지 대체 UI(로딩 상태 등)를 표시할 수 있도록 지원하는 기능이다. Suspense는 React 16.6에서 처음 도입되었으며, 서버 렌더링(SSR)과 클라이언트 렌더링(CSR) 모두에서 활용할 수 있다.


Suspense의 주요 기능

  1. 비동기 상태 관리
    Suspense는 비동기 작업이 완료될 때까지 Fallback UI를 표시하며, 완료되면 원래 컴포넌트를 렌더링한다.

  2. 로딩 UI 처리
    비동기 데이터를 기다리는 동안 스피너, 스켈레톤, "로딩 중" 메시지와 같은 로딩 상태를 간단히 표시할 수 있다.

  3. 클라이언트 및 서버 렌더링 지원
    Suspense는 CSR과 SSR 모두에서 사용할 수 있으며, 서버에서는 데이터 Fetching과의 통합에 특히 유용하다.


Suspense 사용법

기본 사용법

Suspense는 React.Suspense 컴포넌트를 통해 사용할 수 있다.
fallback 속성을 이용하여 비동기 작업 중 표시할 대체 UI를 정의한다.

코드 예제: 클라이언트 측 Suspense

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>
  );
}

동작 설명:

  1. LazyComponent는 동적으로 로드된다(React.lazy 사용).
  2. 컴포넌트가 로드되는 동안 "Loading Component..." 메시지가 표시된다.
  3. 컴포넌트 로드가 완료되면 대체 UI가 원래 컴포넌트로 교체된다.

Suspense와 데이터 Fetching

React 18부터 Suspense는 데이터 Fetching과 통합되어, 서버에서 데이터를 Fetch하는 동안 로딩 상태를 처리할 수 있다.

서버 데이터 Fetching 예제 (Next.js)

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)
  );
}

Suspense로 로딩 상태 처리

import { Suspense } from "react";
import Page from "./Page";

export default function App() {
  return (
    <div>
      <Suspense fallback={<p>Loading Page...</p>}>
        <Page />
      </Suspense>
    </div>
  );
}

Suspense와 서버 컴포넌트(Next.js)

서버 컴포넌트와 Suspense

Next.js에서는 Suspense와 서버 컴포넌트를 함께 사용해 서버에서 데이터를 Fetch하고, 스트리밍 방식으로 렌더링할 수 있다.

예제: Suspense로 구성 요소 분리

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>
  );
}

Suspense의 주요 장점

  1. 로딩 상태 처리 간소화
    별도의 상태 관리 없이 로딩 상태를 처리할 수 있다.

  2. 사용자 경험 향상
    로딩 중에도 의미 있는 대체 UI를 제공하여 빈 화면을 방지한다.

  3. 코드 스플리팅과 통합
    React.lazy를 통해 동적으로 컴포넌트를 로드할 때 대체 UI를 표시할 수 있다.

  4. 서버와 클라이언트 렌더링 통합
    데이터 Fetching과 서버 컴포넌트 렌더링에 유용하게 활용된다.


Suspense 사용 시 주의사항

  1. 지원되는 환경
    React 16.6 이상에서 사용할 수 있다. 서버와 클라이언트 모두에서 Suspense를 제대로 활용하려면 React 18 이상을 권장한다.

  2. 오직 Fallback UI만 지원
    Suspense는 로딩 상태를 관리하지만, 에러 처리는 지원하지 않는다. 에러 처리를 위해서는 ErrorBoundary를 함께 사용해야 한다.

  3. 비동기 작업의 제약
    Suspense는 React 내부에서 지원하는 비동기 작업(예: React.lazy, 서버 컴포넌트)과만 호환된다.


Suspense와 ErrorBoundary 통합

로딩 상태와 에러 처리를 통합하려면 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를 활용한 영화 상세 페이지 예제

아래는 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>
  );
}

0개의 댓글