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 작동이 완전하지 않은 경우도 있는 것 같다.
출처
'개발 > React, NextJs' 카테고리의 다른 글
React classnames 모듈 사용하기 (0) | 2024.03.08 |
---|---|
가치있는 컴포넌트 만들기 (0) | 2024.03.06 |
React에서 inline style을 쓰지 말아야하는 이유 (0) | 2024.02.21 |