React.js 프로젝트를 진행하다보면 크기가 커지게 됩니다. 지난번 작업을 할 때에도 Build 작업을 진행시 다음과 같은 경고 메세지가 출력 되었습니다.

$ yarn    
yarn install v1.22.19

WARNING in asset size limit: 
The following asset(s) exceed the recommended size limit (244 KiB).
This can impact web performance.
Assets: index.js (488 KiB)

WARNING in webpack performance recommendations: 
You can limit the size of your bundles by using import()
or require.ensure to lazy load some parts of your application.

webpack 5.88.0 compiled with 3 warnings in 7037 ms
Done in 7.60s.

처음 작업을 할 때부터 Code Split 내용이 중요하다는 이야기를 듣고는 있었지만 귀찮아서 프로젝트에 적용하지 않고 있었습니다. 이번에 Vite.js 를 미들웨어로 변경했고, 중형 크기의 프로젝트를 진행하면서 적용을 해야겠다는 생각에 내용들을 찾아서 정리해 보았습니다.

How to Improve Performance in React with Code Splitting


React

Lazy Loading

컴포넌트를 호출할 때 React 의 lazy() 함수를 활용하여 호출 합니다. 불러오는 방식에는 2가지가 있는데 함수를 사용하여 바로 호출하는 방법과 Promise 객체를 활용하여 지연시간 을 추가하는 방식이 있습니다.

import { lazy } from "react";
(-) import SampleComponent from './components/Sample';
(+) const SampleComponent = lazy(() => import("./components/Sample"));

React 리액트 라우터 지연시간 걸기 | React suspense/lazy delay? 방식을 활용하면, 모듈이 불완전하게 호출되어 화면이 깨지는 것을 피할 수 있습니다.

import { lazy } from "react";
const SampleComponent2 = lazy(() => {
  return Promise.all([
      import("./components/Sample"),
      new Promise(resolve => setTimeout(resolve, 500))
  ])
  .then(([moduleExports]) => moduleExports);
});

React Router Dom

vite.js 에서는 별도 설정할 내용이 없습니다. lazy 로 호출한 컴포넌트를 Suspense 함수를 사용하여 Router Dom 에 연결을 하고, 지체된 시간동안 Loading 화면에서 제어할 내용을 추가 합니다.

import { lazy, Suspense } from "react";
import { 
  BrowserRouter, Routes, Route, Outlet, Link 
} from "react-router-dom";
...
const App = () => {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Outlet />}>
          <Route
            index
            element={
              <Link to="/sample">lazy sample</Link>
            }
          />
          <Route
            path="sample"
            element={
              <Suspense fallback={
                <div>Loading...</div>
              }>
                <SampleComponent />
              </Suspense>
            }
          />
        </Route>
      </Routes>
    </BrowserRouter>
  );
};

Build

리액트 파일을 빌드하면 다음과 같은 내용을 출력합니다.

$ yarn build
yarn run v1.22.19
warning package.json: No license field
$ tsc && vite build
vite v2.6.14 building for production...
transforming (1) index.htmlBrowserslist: caniuse-lite is outdated. Please run:
  npx browserslist@latest --update-db
✓ 69 modules transformed.

dist/assets/favicon.17e50649.svg         1.49 KiB
dist/index.html                          0.59 KiB
dist/assets/index.acdff5c3.js            3.33 KiB / gzip: 1.64 KiB
dist/assets/react-icons.de186dfe.js      1.48 KiB / gzip: 0.73 KiB
dist/assets/react-spinners.9e109b39.js   2.29 KiB / gzip: 1.15 KiB
dist/assets/Sample.d1ff6fc8.js           0.82 KiB / gzip: 0.50 KiB
dist/assets/index.458f9883.css           0.29 KiB / gzip: 0.22 KiB
dist/assets/vendor.1f2b8393.js           139.33 KiB / gzip: 45.34 KiB
Done in 4.07s.

빌드값을 템플릿에 추가하는 내용까지 자동으로 완성이 됩니다. 템플릿에 포함되는 내용을 살펴보면 다음과 같습니다. index.js index.css 파일과 함께, 연결관계를 확인하는 vendor.1f2b8393.js, 그리고 Loading 화면에서 필요한 react-spinners.9e109b39.js 3개의 내용을 추가하면 됩니다.

<!DOCTYPE html>
  <head>
    <script type="module" crossorigin src="/assets/index.acdff5c3.js"></script>
    <link rel="modulepreload" href="/assets/vendor.1f2b8393.js">
    <link rel="modulepreload" href="/assets/react-spinners.9e109b39.js">
    <link rel="stylesheet" href="/assets/index.458f9883.css">
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>


참고 사이트