Vite 환경에서 Bundle Analyzer로 빌드 최적화하기

김가희·2024년 5월 13일
0

문제 상황


빌드하는 중에 이런 경고 메시지가 떴다.
이 경고는 빌드 프로세스 중에 몇몇 청크(코드 묶음)가 최소화된 후에도 여전히 500kB를 초과한다는 것을 알리는 메시지라고 한다.

큰 프로젝트도 아닌데 빌드 시간이 11.66s ... ... .



문제 원인

어떤 것이 문제인지 확인하기 위해 rollup-plugin-visualizer로 번들의 크기를 시각적으로 확인해 보았다.
아까 문제가 되었던 index.js에서는 firebase가 많은 부분을 차지하고 있고, EventDetail.js에서는 refractor가 많은 부분을 차지하고 있는 것을 알 수 있다.



문제 해결

문제 해결 과정 1

firebase 라이브러리가 상당히 큰 공간을 차지하고 있는 것을 알 수 있다.
그래서 검색해 봤는데 firebase 공식 문서에서 제안한 방법들은 나는 vite를 사용하고 있기 때문에 이미 적용이 되고 있었다…….

// before
import { doc, getDoc, setDoc, updateDoc } from 'firebase/firestore';
// after
import { doc, getDoc, setDoc, updateDoc } from 'firebase/firestore/lite';

일단 실시간 업데이트가 필요하지 않은 서비스는 firestore/lite를 사용하면 번들 사이즈를 좀 줄일 수 있다고 하여 해당 코드를 수정하였다.


dist/assets/index-D1u2acml.js                        746.35 kB │ gzip: 205.89 kB
dist/assets/EventDetail-BHVjT0o1.js                1,050.32 kB │ gzip: 362.96 kB

✓ built in 7.04s

index.js가 처음 빌드 시에 935.29 kB였던 것이 746.35 kB가 되었다.
built 시간이 11.66s에서 7.04s가 되었다.

어느 정도 성공!


문제 해결 과정 2

동적 import를 이용한 코드 스플리팅

터미널 메시지에서 제안해 준 방법대로 Using dynamic import() to code-split the application를 적용해 보았다.

동적 import로 수정한 컴포넌트의 기준은 사용자가 특정 동작을 수행할 때만 필요할 수 있는 컴포넌트이다.
예를 들어 Main.tsx에서는 사용자가 필터 버튼을 눌렀을 때만 보여지는 컴포넌트인 ModalCategory와 ModalPrice, 그리고 에러가 났을 때만 보여지는 컴포넌트인 ErrorBox를 동적 import로 수정했다.

const ErrorBox = lazy(() => import('@shared/ErrorBox'));
const ModalCategory = lazy(() => import('@components/main/ModalCategory'));
const ModalPrice = lazy(() => import('@components/main/ModalPrice'));

dist/assets/index-AaaZZaCI.js                        739.39 kB │ gzip: 203.56 kB
dist/assets/EventDetail-jb-Hc4l9.js                1,050.70 kB │ gzip: 363.12 kB

✓ built in 7.27s

index.js가 746.35 kB였던 것이 739.39 kB가 되었다.
built 시간이 7.04s에서 7.27s가 되었다.

........................ 약간 실패.


문제 해결 과정 3

Rollup의 manualChunks 옵션을 사용하여 청크를 수동으로 분할하는 방법을 적용

빌드 오류에서 제안해 준 방법대로 Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/configuration-options/#output-manualchunks를 적용해 보았다.

export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom', 'react-router-dom'],
          firebase: [
            'firebase/app',
            'firebase/auth',
            'firebase/firestore',
            'firebase/storage',
            'firebase/analytics',
          ],
          ui: [
            '@radix-ui/react-alert-dialog',
            '@radix-ui/react-avatar',
            '@radix-ui/react-checkbox',
            '@radix-ui/react-dropdown-menu',
            '@radix-ui/react-label',
            '@radix-ui/react-popover',
            '@radix-ui/react-radio-group',
            '@radix-ui/react-select',
            '@radix-ui/react-slot',
            '@radix-ui/react-tooltip',
          ],
          syntaxHighlight: ['refractor'],
        },
      },
    },
  },
  // ... 생략
});
  • vendor: ['react', 'react-dom', 'react-router-dom']: 비교적 자주 업데이트되지 않는 라이브러리들 분리. 라이브러리가 변경되지 않는 경우, 이전에 브라우저에 캐싱된 버전을 재사용할 수 있기 때문에 웹사이트의 성능을 개선할 수 있음.
  • firebase: [...], syntaxHighlight: ['refractor']: 사이즈 큰 라이브러리를 별도의 청크로 분리하여 초기 로딩 시에 필요한 자원의 양을 줄이고자 함. 사용자가 처음 페이지를 방문할 때, 필수적이지 않은 자원들은 나중에 필요할 때 비동기적으로 로드할 수 있음.

dist/assets/index-C8WAEFox.js                        280.67 kB │ gzip:  78.15 kB
dist/assets/EventDetail-B6b9m9S7.js                  937.60 kB │ gzip: 323.21 kB

✓ built in 7.62s

index.js가 739.39 kB였던 것이 280.67 kB가 되었다.
EventDetail.js도 1,050.70 kB였던 것이 937.60 kB가 되었다.
built 시간이 7.27s에서 7.62s가 되었다.

성공.


하지만 아직도 EventDetail은 개선이 필요하다. 별도의 청크로 분리를 시도하였으나 의도와는 다르게 refractor가 여전히 초기 로딩 시 로드되고 있는 중이다.


문제 해결 과정 4

라이브러리 교체를 통한 번들 최적화

ChatGPT에 물어보니 다른 라이브러리(@uiw/react-md-editor)에서 내부적으로 refractor를 사용하고 있기 때문에 여전히 번들 크기에 영향을 미치고 있다는 것 같았다.

그래서 나는 refractor를 사용하지 않고 훨씬 더 가벼운 react-markdown로 라이브러리를 대체하였다.


dist/assets/index-Cb2yEKZa.js                        280.60 kB │ gzip: 78.10 kB
dist/assets/EventDetail-BT16ARb2.js                  122.06 kB │ gzip: 38.05 kB

✓ built in 6.15s

index.js가 280.67 kB였던 것이 280.60 kB가 되었다.
EventDetail.js도 937.60 kB였던 것이 122.06 kB가 되었다.
built 시간이 7.62s에서 6.15s가 되었다.

성공.



최종적으로는 웹 애플리케이션의 빌드 시간을 기존 11.66초에서 6.15초로 단축, 47% 개선을 달성하였다!
사용자 경험 향상과 서버 리소스 효율성 증가에 기여하게 된 것이다.

0개의 댓글