[React] React.lazy를 적용하여 코드 분할하기

배성규·2023년 11월 9일

리액트

목록 보기
5/5
post-thumbnail

0. 왜 써야하는가?

대부분의 리액트 앱은 Webpack, Rollup등의 툴을 사용하여 여러 파일을 하나로 병합한 번들된 파일을 우엡 페이지에 포함하여 한 번에 전체 앱을 로드한다.

앱이 커지는 것에 따라 번들도 함께 커진다. 앱의 사이즈가 너무 거대해지면 로드 시간도 길어지는데, 이는 유저 이탈과 같은 행동을 불러온다.
번들 사이즈를 줄이는 좋은 방법으로는 번들을 나누는 것이다.

코드 분할은 런타임에 여러 번들을 동적으로 만들고 불러오는 것으로 이를 통해 앱을 지연 로딩되도록 만들고 사용자에게 획기적인 성능 향상을 제공한다.

앱의 코드 양을 줄이지 않고 당장 필요하지 않은 코드를 불러오지 않도록 하여 초기 로딩에 대한 리소스가 줄어든다.

1. React.lazy와 Suspense

1-1. 적용 전

lazy 함수를 사용하면 동적으로 import하여 컴포넌트를 렌더링할 수 있다.

import React from "react";
import { BrowserRouter, Route, Routes, Navigate } from "react-router-dom";
import { useCookies } from "react-cookie";
import Header from "./components/Header";
import Nav from "./components/Nav";
import Footer from "./components/footer/Footer";
import Main from "./pages/Main";
import Notice from "./pages/Notice";
import KyRecommend from "./pages/KyRecommend";
import Graduate from "./pages/Graduate";
import Mypage from "./pages/Mypage";
import Login from "./pages/Login";
import RouteChangeTracker from "./utils/RouteChangeTracker";
import NoticeDetail from "./pages/NoticeDetail";
import Signup from "./pages/Signup";
import Find from "./pages/Find";
import KyRecommendDetail from "./pages/KyRecommendDetail";
import ModifyInfo from "./pages/ModifyInfo";

function App() {
  const [cookies] = useCookies(["accessToken"]);

  return (
    <BrowserRouter>
      <RouteChangeTracker />
      <Header />
      <Nav />
      <Routes>
        <Route path="/" element={<Main />} />
        <Route path="/notice" element={<Notice />} />
        <Route path="/notice/:id" element={<NoticeDetail />} />
        <Route path="/KyRecommend" element={<KyRecommend />} />
        <Route path="/login" element={<Login />} />
        <Route path="/signup" element={<Signup />} />
        <Route path="/find" element={<Find />} />
        <Route
          path="/KyRecommend/:id"
          element={
            cookies.accessToken ? (
              <KyRecommendDetail />
            ) : (
              <Navigate replace to="/login" />
            )
          }
        />
        <Route
          path="/Graduate"
          element={
            cookies.accessToken ? (
              <Graduate />
            ) : (
              <Navigate replace to="/login" />
            )
          }
        />
        <Route
          path="/Mypage"
          element={
            cookies.accessToken ? <Mypage /> : <Navigate replace to="/login" />
          }
        />
        <Route
          path="/mypage/modifyInfo"
          element={
            cookies.accessToken ? (
              <ModifyInfo />
            ) : (
              <Navigate replace to="/login" />
            )
          }
        />
      </Routes>
      <Footer />
    </BrowserRouter>
  );
}

export default App;
  • SPA 특징 중 하나로 맨 처음 로딩 시, 페이지의 전체 리소스를 다운받는다.
  • 전체 리소스를 다운받으므로 지금 당장 보지 않는 페이지의 리소스를 다운받는 것도 기다려야한다.
  • 리소스를 다운받은 후, 첫 페이지가 렌더링된다.

초기에 보이지 않는 리소스 로딩을 기다릴 이유가 없기 때문에 리소스와 사용자 경험의 향상을 위해 Code-splitting기법과 lazy-loading을 사용한다.

1-2. 적용 후

import React, { Suspense, lazy } from "react";
import { BrowserRouter, Route, Routes, Navigate } from "react-router-dom";
import { useCookies } from "react-cookie";
import RouteChangeTracker from "./utils/RouteChangeTracker";
import Spinner from "./components/Spinner";
const Header = lazy(() => import("./components/Header"));
const Nav = lazy(() => import("./components/Nav"));
const Footer = lazy(() => import("./components/footer/Footer"));
const Notice = lazy(() => import("./pages/Notice"));
const Main = lazy(() => import("./pages/Main"));
const KyRecommend = lazy(() => import("./pages/KyRecommend"));
const Graduate = lazy(() => import("./pages/Graduate"));
const Mypage = lazy(() => import("./pages/Mypage"));
const Login = lazy(() => import("./pages/Login"));
const NoticeDetail = lazy(() => import("./pages/NoticeDetail"));
const Signup = lazy(() => import("./pages/Signup"));
const Find = lazy(() => import("./pages/Find"));
const KyRecommendDetail = lazy(() => import("./pages/KyRecommendDetail"));
const ModifyInfo = lazy(() => import("./pages/ModifyInfo"));

function App() {
  const [cookies] = useCookies(["accessToken"]);

  return (
    <BrowserRouter>
      <RouteChangeTracker />
      <Suspense fallback={<Spinner />}>
        <Header />
        <Nav />
        <Routes>
          <Route path="/" element={<Main />} />
          <Route path="/notice" element={<Notice />} />
          <Route path="/notice/:id" element={<NoticeDetail />} />
          <Route path="/KyRecommend" element={<KyRecommend />} />
          <Route path="/login" element={<Login />} />
          <Route path="/signup" element={<Signup />} />
          <Route path="/find" element={<Find />} />
          <Route
            path="/KyRecommend/:id"
            element={
              cookies.accessToken ? (
                <KyRecommendDetail />
              ) : (
                <Navigate replace to="/login" />
              )
            }
          />
          <Route
            path="/Graduate"
            element={
              cookies.accessToken ? (
                <Graduate />
              ) : (
                <Navigate replace to="/login" />
              )
            }
          />
          <Route
            path="/Mypage"
            element={
              cookies.accessToken ? (
                <Mypage />
              ) : (
                <Navigate replace to="/login" />
              )
            }
          />
          <Route
            path="/mypage/modifyInfo"
            element={
              cookies.accessToken ? (
                <ModifyInfo />
              ) : (
                <Navigate replace to="/login" />
              )
            }
          />
        </Routes>
        <Footer />
      </Suspense>
    </BrowserRouter>
  );
}

export default App;
  • 라우터 수준에서 코드 분할을 했다. 라우터에서 코드 스플리팅을 하는 것을 통해 사용자 경험을 해치지 않으면서 번들을 균등하게 분배했다. 또한, 이는 현재 뷰에 필요한 코드만 다운로드하도록 보장한다.

1-3. Suspense

lazy로 import하여 가져오는 동안 표시할 항목도 지정해야하는데, Suspense를 사용하여 이를 지정한다.

<Suspense fallback={<Spinner />}>
   <Route path="/" element={<Main />} />
</Suspense>
  • 메인 컴포넌트가 로딩되기 전에 스피너 컴포넌트를 보여주어 사용자 이탈을 막는다.

1-4. 주의

너무 과도하게 분리된 코드는 http 요청 수를 증가시키므로 성능 이점이 상쇄될 수 있다.


참고자료

React docs
wiki docs
기억보다 기록을

profile
FE 유망주🧑‍💻

0개의 댓글