[React] React.Suspense에 대하여

박기영·2023년 1월 18일
3

React

목록 보기
23/32
post-custom-banner

페이지를 렌더링할 때 아직 준비가 안된 경우 로딩 창을 보여줄 수 있는 기능인 Suspense!
UX를 향상시키기 위한 아주 간단한 방법이라고 생각되므로, 알아보도록 하자.

개요

Suspense lets components “wait” for something before rendering. Today, Suspense only supports one use case: loading components dynamically with React.lazy. In the future, it will support other use cases like data fetching.
- React 공식 docs -

Suspense를 사용하면 컴포넌트가 렌더링하기 전에 다른 작업이 먼저 이루어지도록 “대기합니다”. 현재 Suspense는 단 하나의 사용 사례 React.lazy를 사용하여 컴포넌트를 동적으로 불러오기만 지원합니다. 나중에는 데이터 불러오기와 같은 사용 사례를 지원할 계획입니다.
- React 공식 docs -

공식 문서를 확인해보니 현재 v18.2.0 기준,
SuspenseReact.lazy와 함께 사용하여 동적으로 컴포넌트를 로딩하는 것만이 지원된다고 한다.

흠..컴포넌트가 렌더링 되기 전에 다른 작업이 이루어지도록 해준다는데...
어떤 작업을 말하는걸까?

React.Suspense

공식 문서에서는 아래와 같은 예시를 보여준다.

import React, { Suspense } from 'react';

// This component is loaded dynamically
const OtherComponent = React.lazy(() => import('./OtherComponent'));

function MyComponent() {
  return (
    // Displays <Spinner> until OtherComponent loads
    <React.Suspense fallback={<Spinner />}>
      <div>
        <OtherComponent />
      </div>
    </React.Suspense>
  );
}

같이 적혀 있는 설명과 함께 살펴보자.

React.Suspense lets you specify the loading indicator in case some components in the tree below it are not yet ready to render.
- React 공식 docs -

React.Suspense는 트리 하단에 있는 일부 컴포넌트가 아직 렌더링할 준비가 되지 않은 경우 로딩 지시기 (Loading indicator)를 나타낼 수 있도록 해줍니다.
- React 공식 docs

React.Suspense는 컴포넌트가 아직 렌더링 준비가 되지 않았을 때,
특정한 컴포넌트를 보여주는 기능을 가지고 있다고한다.

위 코드를 가지고 이해를 해보자면,
OtherComponent가 렌더링 준비가 안된 상태라면 Spinner 컴포넌트를 보여준다는 것이다.

그런데...OtherComponent를 감싸고 있는 React.lazy는 뭘까?

React.lazy

React.lazy() lets you define a component that is loaded dynamically. This helps reduce the bundle size to delay loading components that aren’t used during the initial render.
- React 공식 docs -

React.lazy()를 사용하면 동적으로 불러오는 컴포넌트를 정의할 수 있습니다. 그러면 번들의 크기를 줄이고, 초기 렌더링에서 사용되지 않는 컴포넌트를 불러오는 작업을 지연시킬 수 있습니다.
- React 공식 docs -

초기 렌더링에 필요하지 않은 컴포넌트들을 불러오는 것을 지연시켜서,
번들의 크기를 줄일 수 있다고 한다.

The lazy component should then be rendered inside a Suspense component, which allows us to show some fallback content (such as a loading indicator) while we’re waiting for the lazy component to load.
- React 공식 docs

lazy 컴포넌트는 Suspense 컴포넌트 하위에서 렌더링되어야 하며, Suspense는 lazy 컴포넌트가 로드되길 기다리는 동안 로딩 화면과 같은 예비 컨텐츠를 보여줄 수 있게 해줍니다.
- React 공식 docs -

React.lazy()로 불러오는 컴포넌트는 React.Suspense는 내에서 사용해줘야한다.
React.lazy()로 인해 동적으로 불러와지는 컴포넌트가 불러와지는 것을 기다리면서,
React.Suspensefallback에 들어있는 컴포넌트를 보여주는 것이다.

여러 개의 컴포넌트에 lazy, Suspense 적용

그러면, React.lazy()로 불러오는 컴포넌트는 하나하나 React.Suspense로 감싸줘야하나요??
다행히도, Suspense로 여러 lazy 컴포넌트를 감싸줄 수 있다.

import React, { Suspense } from 'react';

const OtherComponent = React.lazy(() => import('./OtherComponent'));
const AnotherComponent = React.lazy(() => import('./AnotherComponent'));

function MyComponent() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <section>
          <OtherComponent />
          <AnotherComponent />
        </section>
      </Suspense>
    </div>
  );
}

이러면 OtherComponent, AnotherComponent의 렌더링 준비가 덜 되었을 때
Suspensefallback에 있는 컴포넌트를 보여주게 될 것이다.

아래 영상은 OtherComponent만 렌더링하도록 수정한 코드를 새로고침하는 것을 보여준다.

참고 동영상

OtherComponent가 렌더링이 되기 위한 준비가 되기 전에는
fallback에 들어있는 컴포넌트가 보여지는 것을 확인할 수 있다.

Route와 같이 사용해보기

React로 프로젝트를 만들면 SPA 페이지를 만들기 위해서 react-router-dom을 사용할 것이다.
필자는 각각의 RouteSuspense를 적용하고 싶었다.
해당 페이지가 렌더링 준비가 덜 되었다면 fallback이 보이도록 말이다.

import React, { Suspense } from "react";
import { Routes, Route, BrowserRouter } from "react-router-dom";

import { AuthContext } from "./context/auth-context";
import { useAuth } from "./hoc/auth-hook";
import LoadingSpinner from "./shared/LoadingSpinner";

const LandingPage = React.lazy(() => import("./pages/LandingPage/LandingPage"));
const CompanyPage = React.lazy(() => import("./pages/CompanyPage/CompanyPage"));
const ArtistPage = React.lazy(() => import("./pages/ArtistPage/ArtistPage"));
const BusinessPage = React.lazy(
  () => import("./pages/BusinessPage/BusinessPage")
);
const MyPage = React.lazy(() => import("./pages/MyPage/MyPage"));
const LoginPage = React.lazy(() => import("./pages/LoginPage/LoginPage"));
const JoinPage = React.lazy(() => import("./pages/LoginPage/JoinPage"));

function App() {
  const { token, login, logout, userId } = useAuth();

  return (
    <BrowserRouter basename={process.env.PUBLIC_URL}>
      <AuthContext.Provider
        value={{ isLoggedIn: !!token, token, userId, login, logout }}
      >
        <Suspense fallback={<LoadingSpinner />}>
          <Routes>
            <Route path="/" element={<LandingPage />} />
            <Route path="/company" element={<CompanyPage />} />
            <Route path="/artist" element={<ArtistPage />} />
            <Route path="/business" element={<BusinessPage />} />
            <Route path="/mypage/:userId" element={<MyPage />} />
            <Route path="/login" element={<LoginPage />} />
            <Route path="/join" element={<JoinPage />} />
          </Routes>
        </Suspense>
      </AuthContext.Provider>
    </BrowserRouter>
  );
}

export default App;

위와 같은 코드를 작성했고, 결과는 다음과 같았다.

참고 동영상

페이지가 이동할 때마다 LoadingSpinner가 보이는 것을 확인할 수 있다.

앞서, React.lazy()는 초기 렌더링에 필요하지 않은 컴포넌트를 불러오는 걸 지연시킨다고했다.
따라서, LandingPage를 보여주다가 다른 페이지로 이동할 때,
컴포넌트를 불러오고, 컴포넌트가 렌더링될 준비를 마치기 전까지 LoadingSpinner가 보인다.

또한, 한번 렌더링이 완료된 페이지는 다시 들어가더라도 Suspense가 작동하지 않는다.
아래 영상을 보자.

참고 동영상

처음 렌더링 시에만 Suspense가 작동하고, 그 이후에는 작동하지 않는 것을 볼 수 있다.

참고 자료

React 공식 docs - React.lazy()
React 공식 docs - React.Suspense
daleseo님 블로그
bbaa3218님 블로그

profile
나를 믿는 사람들을, 실망시키지 않도록
post-custom-banner

0개의 댓글