Lighthouse를 활용한 Tree Shaking(with Build)

hxxtae

Heetae Kim

Posted on May 21, 2024

Lighthouse를 활용한 Tree Shaking(with Build)

프로젝트를 마치고 Build를 진행하고 나서 다음과 같은 결과가 나왔다.

Image description

흠... 좋은데? 🤦‍♂️

빌드된 파일도 크고 빌드 시간도 길다. 이를 해결하는 과정을 진행해 보자.

마침 Lighthouse를 통해 LCP 시간을 줄이고 있는데 Treemap을 활용해서 Tree Shaking을 진행해보고 싶었다.

Lighthouse 탭에서 스크롤을 내리다 보면 View Treemap 버튼이 있다.

아래 사진을 보면 빌드된 각 파일의 크기를 알 수 있다.

Image description

  • @mui/icons-material
  • @mui-material
  • firebase/firestore
  • devs/chunk
  • react-dom
  • ...

 

라이브러리 경량화1 (MUI)

필자는 프로젝트에 mui 를 사용하였다. 그런데 거의 대부분 컴포넌트 및 파일에서 모듈 전체 멤버를 import하여 필요한 컴포넌트를 불러와 사용하였다.

당시에는 몰랐지만 import { AbcRounded } from '@mui/icons-material' 처럼 가져오게되면, @mui/icons-material에서 내보내고있는 모든 모듈이 번들 사이즈에 포함된다.

필요한 모듈만 사용하기 위해 @mui/icons-material@mui-material 라이브러리를 default export로 정의된 경로만 import하는 방식으로 변경해 주고 다시 Treemap 확인!

아래 이미지는 @mui/icons-material 만 진행한 이미지이다.
@mui-material 번들 사이즈는 다음 이미지를 확인

Image description

결과

@mui/icons-material: 6.1MiB -> 1.8KiB
@mui-material: 1.5MiB -> 711.3KiB

 

라이브러리 경량화2 (Firebase)

Treemap을 보면서 다음으로 큰 비중을 차지하고 있는 Firebase가 보였다.

검색을 통해 찾은 결과 많은 firebase SDK를 사용하고 있다면 다음과 같은 방법으로 SDK를 경량화 할 수 있다고 한다.

해당 솔루션이 바로 Firestore Lite 이다.

그리고 다음과 같이 모든 파일에 존재하는 경로를 변경해 주었다.



// as-is
import { getFirestore } from 'firebase/firestore';

// to-be
import { getFirestore } from 'firebase/firestore/lite';


Enter fullscreen mode Exit fullscreen mode

그리고 Treemap 확인!

Image description

결과

firebase/firestore: 519.6KiB -> 172.0KiB

주의할 점으로 지원되지 않는 API 기능들도 있으니 확인하고 적용하면 되겠다.

또한 Firestore Lite는 실시간 업데이트와 연결이 필요 없는 앱에 적합하다고 한다.

그리고 나서 프로젝트 빌드 실행!

Image description

빌드 결과

빌드 크기: 1,074KB -> 803.93KB
빌드 시간: 1m 46s -> 6.20s

이 정도면 어느 정도는 빌드 최적화를 수행한 것 같지만 아직 한 가지 더 할 것이 남아있다.

 

빌드 경량화

파일을 빌드하면 다음과 같은 경고 문구가 뜬다



- Using dynamic import() to code-split the application
- Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/configuration-options/#output-manualchunks
- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.


Enter fullscreen mode Exit fullscreen mode

Using dynamic import() to code-split the application 방법은 이미 적용 되었으니 다음 방법으로 시도해 보려고 한다.

내용을 번역해 보면, 청크 개선을 위해build.rollupOptions.output.manualChunks 사용하라고 한다. 그 방법은 여기 에 있다고 한다.

즉, 청크 파일을 Code Split 하여 빌드 크기를 줄일 수 있다는 의미로 해석된다. 그래서 청크파일 중 크기가 가장 큰 파일을 분할해 주었다.



// vite.config.js
build: {
  rollupOptions: {
    output:{
      manualChunks(id) {
        if (id.includes('node_modules')) {
          return id.toString().split('node_modules/')[1].split('/')[0].toString();
        }
      }
    }
  }
}


Enter fullscreen mode Exit fullscreen mode

블록을 분해하여 큰 블록을 더 작은 블록으로 나누는 방법이라고 한다. 이렇게 전부 코드를 분할하는 방식 보다는 다음과 같은 방식으로 변경해 주었다.



// vite.config.js
build: {
  rollupOptions: {
     output: {
       manualChunks: {
         firebase: [
           'firebase/app',
           'firebase/auth',
           'firebase/storage',
           'firebase/firestore',
          ],
          react: [
            'react-dom',
            'react-query',
            'react-router-dom'
          ],
        }
     },
  },
},


Enter fullscreen mode Exit fullscreen mode

큰 사이즈의 라이브러리들만 골라 선언해 주었다.

큰 사이즈의 라이브러리는 사용자가 처음 페이지를 방문할 때 필요 없는 자원들을 모두 불러오기 때문에 초기 로딩 속도를 지연시킬 수 있기 때문에 큰 사이즈 라이브러리를 대상으로 코드 분할해 주었다.

그리고 빌드 후 확인!

Image description

빌드 결과

빌드 크기: 803.93KB -> 289KB
빌드 시간: 6.20s -> 5.57s

 

마무리

최종적으로 빌드 시간이 단축되고 크기도 줄어서 사용자에게 최적화된 렌더링 경험을 줄 수 있게 되었다.

이 모든 작업을 요약하면 Tree ShakingCode Split 만으로도 좋은 최적화 결과를 얻을 수 있다는 사실을 알게 되었다.

Image description

💖 💪 🙅 🚩
hxxtae
Heetae Kim

Posted on May 21, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related