본문 바로가기
개발/React, NextJs

Suspense란 무엇인가

by 쓱싹디벨로퍼 2024. 3. 7.

React Spense란 무엇인가?

<Suspense>를 사용하면 자식이 로딩을 완료할 때까지 폴백을 표시할 수 있다.

 

기본 사용법

<Suspense fallback={<Loading />}>
  <SomeComponent />
</Suspense>

 

여기서 fallback은 로딩이 완료되지 않은 경우 실제 UI 대신 렌더링 할 대체 UI라는 뜻이다. 유효한 어떤 React 노드든 사용가능하며, 주로 로딩 스피너나 스켈레톤 같은 가벼운 플레이스홀더 뷰를 사용한다. Suspense는 children이 일시 중단되면 자동으로 fallback으로 전환되고, 데이터가 준비되면 다시 children으로 전환된다. 렌더링 중 fallback이 일시 중단되면 가장 가까운 상위 Suspense 경계가 활성화된다.

 

아래 예시에서 앨범 목록을 가져오는 동안 Albums 컴포넌트가 일시 중단된다. 렌더링할 준비가 될 때까지 React는 가장 가까운 상위 Suspense 경계를 폴백인 Loading 컴포넌트로 전환하여 보여준다. 이후 데이터가 로드되면 React는 Loading 폴백을 숨기고 데이터와 함께 Albums 컴포넌트를 렌더링 한다.

import { Suspense } from 'react';
import Albums from './Albums.js';

export default function ArtistPage({ artist }) {
  return (
    <>
      <h1>{artist.name}</h1>
      <Suspense fallback={<Loading />}>
        <Albums artistId={artist.id} />
      </Suspense>
    </>
  );
}

function Loading() {
  return <h2>🌀 Loading...</h2>;
}

 

NextJS 13+ App Router에서의 Suspense의 사용법

Suspense는 NextJS 13 app dir에서 fetch, promise 작업 시에 사용하기 좋다.

여기서 주의할 점은, React.lazy + Suspense 방식은 서버 렌더링 프레임워크에서는 제대로 작동하지 않을 수 있다. 그래서 NextJS에서는 위험한(?) lazy 함수 대신, 아래에 설명할 방법을 쓰면 된다.

 

Async 서버 컴포넌트에 적용하기

export default async function SlowServerComponent() {
	const got = await fetch('<HOME_URL>/api', {
		cache: 'no-store',
		}).then((res) => (res.ok ? res.json() : { message: 'GET failed' }));
	return <div>로딩 완료: {got.message}</div>;
}

import { Suspense } from 'react';
import SlowServerComponent from './SlowServerComponent';

export default function Page() {
	return (
		<Suspense fallback={<div>loading...</div>}> {/* @ts-expect-error Async Server Component */} <SlowComponent /> </Suspense>
	);
}
  • cache를 'no-store'로 설정해야 NextJS가 응답을 캐시 하는 걸 막을 수 있다. 만약 NextJS가 응답을 캐시 하면, 새로고침 했을 때 Suspense가 기능하는 모습 확인이 어려울 수도 있다.
  • 아직 타입스크립트에서 Async Server Component를 동기 컴포넌트 안에 넣는 걸 막고 있기 때문에, {/* @ts-expect-error Async Server Component */}를 비동기 서버 컴포넌트 바로 위에 붙여줘야 한다.

 

클라이언트 컴포넌트에 적용하기

'use client';// 클라이언트 컴포넌트임을 선언.import { use } from 'react';

export default function SlowClientComponent() {
	const got = use(
		fetch('<HOME_URL>/api', {
			cache: 'no-store',
		}).then((res) => (res.ok ? res.json() : { message: 'GET failed' }))
	);
	return <div>로딩 완료: {got.message}</div>;
}

이번에 새로 도입된 'use' 훅을 사용해서 GET 요청을 해보았다. use훅은 클라이언트 컴포넌트에서 fetch 등 Promise 작업을 할 때 사용하기 좋다.

'use client'; // 클라이언트 컴포넌트임을 선언.
import { Suspense } from 'react';
import SlowClientComponent from './SlowClientComponent';

export default function SlowClientPage() {
	return (
		<div> <Suspense fallback={<div>loading...</div>}> <SlowClientComponent /> </Suspense> </div>
	);
}

클라이언트 컴포넌트도 같은 방식으로 작성해서 잘 작동한다.

Suspense가 의도한 대로 동작하지 않을 때

node.js 버전, 컨테이너 환경 확인해 보기

NextJS 13.4+ App Router는 nodejs 16.8부터 지원한다.

 

브라우저, 운영체제 문제

iPad Chrome에서는 페이지 간 이동에서는 fallback이 잘 보이는데 hard navigation을 할 경우에는 보이지 않고 로딩이 완료되었다. (fallback만 안 보이고 페이지 최종 결과물 자체는 잘 불러와지긴 했다.) App router 도입 초반이라 그런지 브라우저 및 운영체제에 따라 Streaming SSR, Suspense 작동이 완전하지 않은 경우도 있는 것 같다.

 

 

출처

https://react-ko.dev/reference/react/Suspense

https://jisiq.com/nextjs/nextjs-suspense

728x90