번들 분석으로 CRA 성능 개선하기
문제점
기능 구현이 거의 완료된 SPA의 메인화면을 lighthouse로 분석했다. 그 결과 largest contentful paint(LCP)와 total blocking time이 안좋다는 것을 알 수 있었다.
LCP가 저하되는 원인은 느린 서버 응답시간, 자바스크립트와 css 렌더 블로킹, 느린 리소스 로딩 시간, 클라이언트 측 렌더링이 있다. 이 중에서도 클라이언트 측 렌더링이 현재 가장 큰 원인이라고 판단했다. SPA의 커다란 자바스크립트 번들을 최적화한다면 의미있는 성능 향상을 얻을 수 있을 것 같았다.
클라이언트 측 렌더링을 최적화하기 위해 다음 사항들을 고려할 수 있다.
- 중요 JavaScript 최소화: JavaScript 축소, 사용하지 않는 JavaScript 지연 등으로 최적화 할 수 있다.
- 서버측 렌더링 사용: Next.js와 같은 프레임워크를 사용해 주요 콘텐츠를 서버에서 렌더링하도록 하는 방법을 사용할 수 있지만, 대규모 코드 변경이 필요해 고려하지 않았다.
JavaScript 최소화를 위해 먼저 번들 분석을 했다.
해결기
'webpack-bundle-analyzer' 플러그인을 사용하면 빌드된 번들을 분석할 수 있다. 앱을 CRA로 만들어서 eject
없이 사용하기 위해 cra-bundle-analyzer
를 사용했다.
결과는 다음과 같았다.
메인이 무려 5.3MB나 된다. 그 중 @codemirror
란 라이브러리가 거의 1MB를 차지하고 있었다. 메인화면에서는 사용하지도 않는 라이브러리인데 말이다.
main을 좀 더 가볍게 하기위해 code splitting을 했다. Codemirror를 사용하는 페이지들을 lazy
와 Suspense
를 사용해 필요할때만 불러오는 것이다.
1// ...2const Solve = lazy(() => import("./components/pages/Solve"));3const Solution = lazy(() => import("./components/pages/Solution"));4// ...5function App() {6 return (7 <QueryClientProvider client={queryClient}>8 <BrowserRouter>9 <Suspense fallback={<Loading />}>10 {...}11 </Suspense>12 </BrowserRouter>13 </QueryClientProvider>14 );15}
이것만 변경하고 다시 확인해봤다. 결과는 놀라웠다.
main의 크기를 2.2MB로 거의 절반으로 줄일 수 있었다. LCP와 블락킹 타임도 개선된 것을 확인할 수 있다.
참조: