-
React + Vite 초기 로딩 최적화Project 2023. 12. 8. 17:54
SPA의 초기 로딩 문제점
React는 SPA기반입니다. 때문에 최상위 루트 index.html 파일에 위치하는 스크립트에 모든 모듈이 담겨 있습니다.
이로써 페이지 초기 진입 시, 당장 사용하지 않는 모듈까지 로드하기 때문에 초기 로딩 속도가 느린 문제가 있습니다.
초기 로딩 속도 개선을 위해 위해 두 가지를 생각했습니다.
- vite의 splitVendorChunkPlugin & manualChunks 활용
- 여러 chunks로 분할하여, 비동기 청크 로드 가능
- React의 lazy 활용하여 dynamic import 적용
- 초기 진입 시, 불필요한 모듈은 불러오지 않게 하며 앱의 초기화 로딩에 필요한 비용을 줄여줍니다.
1. vite의 splitVendorChunkPlugin & manualChunks 활용
index size : 939.31kb
build 완료 시, size 500kb 보다 클 경우 고려해 볼 점에 대한 안내 메세지를 확인할 수 있습니다.
- using dynamic import
- use build.rollupOptions.output.manualChunks to imporve chunking[ Vite 공식문서를 참고하여 chunk를 분리하여 비대한 index size를 분산하는 방법을 확인할 수 있습니다. ]
vite config에 splitVendorChunkPlugin()를 적용하여 index와 vendor로 청크를 분할할 수 있습니다.
index와 vendor로 분리 index size : 48.16kb
vendor size : 891.29kb결과를 확인해 보니, index와 vendor로 나뉜 것을 확인할 수 있습니다.
하지만 index의 size는 크게 줄었지만, vendor size가 비대하여 단순히 index에서 vendor로 옮긴 것뿐인 것 같습니다.비대한 vendor를 해결하기 위해서,
앞서 Vite 안내 메세지에서 언급한 rollupOptions.output.manualChunks를 사용하여 분산시킵니다.// vite.config.ts import { defineConfig } from "vite"; import { splitVendorChunkPlugin } from "vite"; import react from "@vitejs/plugin-react-swc"; export default defineConfig({ plugins: [react(), splitVendorChunkPlugin()], build: { rollupOptions: { output: { manualChunks(id: string) { console.log(id); // manualChunks에 어떤 것들이 출력되는지 확인해 봤습니다 :-) }, }, }, }, });
manualChunks에 어떤 것들이 출력되는지 확인해 봤습니다 :-)
vendor에 있는 모든 모듈의 경로가 출력됩니다.
초기 index에 있던 모듈이 vendor로 이동한 것을 확인할 수 있습니다.
아래 코드를 통해 사이즈가 다소 큰 라이브러리는 vendor에서 분리를 시도했습니다.
export default defineConfig({ plugins: [react(), splitVendorChunkPlugin()], build: { rollupOptions: { output: { manualChunks(id: string) { if (id.includes("firebase")) { return "@firebase"; } if (id.includes("date-fns")) { return "@date-fns"; } if (id.includes("react-router-dom") || id.includes("react-router")) { return "@react-router"; } if (id.includes("react-datepicker")) { return "@react-datepicker"; } if (id.includes("tanstack")) { return "@react-query"; } }, }, }, }, });
초기 index에서 모든 모듈을 관리했는데, veondor와 chunks로 분산했습니다.
2. React의 lazy 활용하여 dynamic import 적용
React.lazy는 동적 import를 호출하는 함수를 인자로 가집니다.
lazy 컴포넌트는 Suspense 하위에서 렌더링 되어야 합니다.공식문서에서 안내하는 방법에 따라 적용했습니다.
- React.lazy는 동적 import를 호출하는 함수를 인자로 가지고, import에는 컴포넌트의 경로를 넘겨줍니다.
- lazy 컴포넌트는 Suspense 하위에서 렌더링 되어야 하며,Suspense는 lazy 컴포넌트가 로드되길 기다리는 동안 로딩 화면과 같은 예비 컨텐츠를 보여줄 수 있게 해 줍니다.
import { Suspense, lazy } from "react"; const Calendar = lazy(() => import("../pages/calendar/Calendar.tsx")); const Record = lazy(() => import("../pages/record/Record.tsx")); export const RouterInfo = [ { path: "/", element: <Layout />, children: [ // - - - - - - { path: PATH_NAME.CALENDAR, element: ( <Suspense fallback={<Loading />}> <Calendar /> </Suspense> ), label: "calendar", }, { path: PATH_NAME.RECORD, element: ( <Suspense fallback={<Loading />}> <Record /> </Suspense> ), label: "record", }, // - - - - - - ], }, ];
결과
초기 측정 -> vite의 splitVendorChunkPlugin & manualChunks 활용 후 -> React.lazy [ dynamic import ] 활용 후 순서입니다.
LCP를 기준으로 6.2s → 5.5s → 3.6s의 변화를 확인할 수 있습니다.
'Project' 카테고리의 다른 글
[ Project ] FormData ( File과 Json data를 함께 ) (0) 2023.10.19 [ Project ] 컴포넌트 설계에 대한 고민 (Feat. 합성 컴포넌트) (1) 2023.09.10 [ Project ] Axios Interceptor 활용하기 (0) 2023.09.06 [ Project ] RTK-Query에 Axios 입히기 (0) 2023.09.04 [ Project ] React 재사용 버튼 컴포넌트 만들기 (0) 2023.09.01 - vite의 splitVendorChunkPlugin & manualChunks 활용