페이지를 렌더링할 때 아직 준비가 안된 경우 로딩 창을 보여줄 수 있는 기능인
Suspense
!
UX를 향상시키기 위한 아주 간단한 방법이라고 생각되므로, 알아보도록 하자.
Suspense
lets components “wait” for something before rendering. Today, Suspense only supports one use case: loading components dynamically withReact.lazy
. In the future, it will support other use cases like data fetching.
- React 공식 docs -
Suspense
를 사용하면 컴포넌트가 렌더링하기 전에 다른 작업이 먼저 이루어지도록 “대기합니다”. 현재 Suspense는 단 하나의 사용 사례React.lazy
를 사용하여 컴포넌트를 동적으로 불러오기만 지원합니다. 나중에는 데이터 불러오기와 같은 사용 사례를 지원할 계획입니다.
- React 공식 docs -
공식 문서를 확인해보니 현재 v18.2.0
기준,
Suspense
는 React.lazy
와 함께 사용하여 동적으로 컴포넌트를 로딩하는 것만이 지원된다고 한다.
흠..컴포넌트가 렌더링 되기 전에 다른 작업이 이루어지도록 해준다는데...
어떤 작업을 말하는걸까?
공식 문서에서는 아래와 같은 예시를 보여준다.
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()
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.Suspense
의 fallback
에 들어있는 컴포넌트를 보여주는 것이다.
그러면, 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
의 렌더링 준비가 덜 되었을 때
Suspense
의 fallback
에 있는 컴포넌트를 보여주게 될 것이다.
아래 영상은 OtherComponent
만 렌더링하도록 수정한 코드를 새로고침하는 것을 보여준다.
OtherComponent
가 렌더링이 되기 위한 준비가 되기 전에는
fallback
에 들어있는 컴포넌트가 보여지는 것을 확인할 수 있다.
React로 프로젝트를 만들면 SPA 페이지를 만들기 위해서 react-router-dom
을 사용할 것이다.
필자는 각각의 Route
에 Suspense
를 적용하고 싶었다.
해당 페이지가 렌더링 준비가 덜 되었다면 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님 블로그