[Next.js] 번들 사이즈 최적화 하는 방법

100Gyeon·2023년 3월 29일
13

Next.js

목록 보기
2/2
post-thumbnail

1. 설치

Next.js 프로젝트에서 번들 파일이 어떤 코드로 이루어져 있는지 알고 싶다면, webpack-bundle-analyzer를 사용하는 리액트와 다르게 @next/bundle-analyzer를 사용해야 합니다.
개발 환경에서 사용할 것이므로 개발 의존성 모드로 설치합니다.

$ yarn add -D @next/bundle-analyzer

2. 관련 설정 추가

next.config.js를 보면 기본적으로 아래와 같은 내용이 있을 것입니다.

const nextConfig = {
  reactStrictMode: true,
  swcMinify: true,
}

module.exports = nextConfig

bundle analyzer에 필요한 부분을 추가합니다.

const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
  openAnalyzer: false,
});

const nextConfig = {
  reactStrictMode: false,
  swcMinify: true,
};

module.exports = withBundleAnalyzer(nextConfig);

3. analyze 명령 시에만 동작하도록 설정

package.json의 scripts 부분을 수정해 analyze 명령 시에만 번들 사이즈를 분석하도록 설정합니다.

"scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "analyze": "ANALYZE=true next build" // 추가
},

만약 윈도우에서 개발하고 있다면, 위의 설정만으로는 yarn analyze가 동작하지 않습니다. 윈도우에서는 아래와 같은 상황이 발생합니다.

이때 cross-env로 해결할 수 있으며, 아래와 같은 추가 설정이 필요합니다.

$ yarn add -D cross-env
"scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "analyze": "cross-env ANALYZE=true next build" // 추가
},

4. 번들 사이즈 분석

이제 모든 설정은 끝났습니다. yarn analyze를 입력하면 아래와 같은 내용을 확인할 수 있습니다.

.next 폴더도 하나 생성되는데, 그 안에 있는 analyze 폴더를 보면 client.html, edge.html, nodejs.html이 추가된 것을 확인할 수 있습니다.

client.html에는 client side의 번들 파일과 그 안에 있는 모든 패키지가 트리맵으로 시각화되어 있습니다. 파일의 실제 크기를 비율로 보여주기 때문에 각각의 패키지가 용량을 얼마만큼 차지하는지 파악하기 좋습니다.

사이즈가 유독 큰 패키지가 무엇인지 파악하고, 대체 혹은 제거할 수 있는 방법을 찾으면 번들 사이즈 최적화가 가능합니다. 제 경우에는 lottie.js가 왼쪽 하단의 큰 부분을 차지하고 있었습니다. 기존에는 lottie-react를 사용하고 있었는데, 더 가벼운 라이브러리인 lottie-light-react로 대체해 봤습니다. yarn analyze했더니 아래과 같이 lottie.js가 사라진 client.html이 생성되었습니다.

5. 코드 스플리팅

하나로 합쳐진 번들 파일을 여러 조각으로 분리해 필요할 때만 따로 로드하기 위해 코드 스플리팅 기법을 이용했습니다.
페이지 초기 로드 시 필요하지 않은 컴포넌트(ex. 유저와 페이지가 상호 작용한 후에 나타나는 모달 컴포넌트)는 dynamic import를 사용해 코드 스플리팅을 적용할 수 있습니다.

import ConfirmModal from '@src/components/learnDetail/ConfirmModal';
import GuideModal from '@src/components/learnDetail/GuideModal';
import ContextMenu from '@src/components/learnDetail/ContextMenu';
import LoginModal from '@src/components/login/LoginModal';

기존에는 위와 같이 import하던 컴포넌트들을 아래와 같이 수정했습니다.

import dynamic from 'next/dynamic';

const ContextMenu = dynamic(() => import('@src/components/learnDetail/ContextMenu'), { ssr: false });
const GuideModal = dynamic(() => import('@src/components/learnDetail/GuideModal'), { ssr: false });
const ConfirmModal = dynamic(() => import('@src/components/learnDetail/ConfirmModal'), { ssr: false });
const LoginModal = dynamic(() => import('@src/components/login/LoginModal'), { ssr: false });

이처럼 dynamic import를 사용하면 빌드할 때가 아닌 런타임에 해당 모듈을 로드하게 되어 초기 로드 속도가 빨라집니다.

6. 최종 결과 확인

대용량 라이브러리를 대체하고, dynamic import를 사용해 코드 스플리팅을 적용한 결과는 다음과 같습니다.

번들 사이즈를 조금이나마 줄여 성능을 개선할 수 있었습니다.

0개의 댓글