js 번들 분석 및 최적화하기

JS 번들의 영향도

  • 첫 화면이 느리다(LCP↑), 초기 JS 다운로드가 크다
  • 특정 페이지를 안 열어도 3D/에디터/그래프 같은 무거운 라이브러리가 항상 내려온다
  • 라우트 이동 때마다 같은 라이브러리가 중복 로드된다

Next.js 번들 생성 특징 및 주의사항

  • 기본은 서버 컴포넌트, 클라이언트 컴포넌트 하위는 전부 번들로 합쳐짐.
  • 즉 클라이언트쪽에서 큰 라이브러리를 사용할 때 주의할 것.
  • app router는 경로별로 별도 청크를 만듦.
  • 동적 import를 사용하면 지연로딩 청크를 따로 생성함.
  • Next/link 를 사용시 자동으로 프리패치됨으로 다음 페이지 청크를 미리 받아서 빠르게 로드가능.

설정코드

package.json

플러그인 실행하는 명령어 설정 @next/bundle-analyzer

{
  "scripts": {
    "analyze": "cross-env ANALYZE=true next build"
  },
}

next.config.mjs

플러그인 세팅

import bundleAnalyzer from '@next/bundle-analyzer';

/** @type {import('next').NextConfig} */
const nextConfig = {
	//... config 설정
};

// 번들 분석 플러그인
const withBundleAnalyzer = bundleAnalyzer({
  enabled: process.env.ANALYZE === "true",
});

// Wrap MDX and Next.js config with each other
export default withBundleAnalyzer(nextConfig);

실제 적용코드

import dynamic from 'next/dynamic';
import type { RigidBody } from '@dimforge/rapier3d-compat'; // 무거운 3d타입만 적용시 

// 무거운 위젯(차트/맵/에디터 등)
const HeavyChart = dynamic(() => import('./HeavyChart'), {
  loading: () => <p>차트를 불러오는 중…</p>,
});

// ssr 제외 (브라우저 전용 라이브러리를 가져올 때)
const Map = dynamic(() => import('./Map'), { ssr: false });


// Suspense 를 통해서 무거운 내용만 따로 로딩
import { Suspense } from 'react';
import dynamic from 'next/dynamic';

const Heavy = dynamic(() => import('./Heavy'), { ssr: false });

export default function Page() {
  return (
    <>
      <Hero /> {/* 가벼운 상단 */}
      <Suspense fallback={<Skeleton />}>
        <Heavy /> {/* 아래쪽은 늦게 */}
      </Suspense>
    </>
  );
}

예시

아래 화면은 next/bundle-analyzer로 분석한 번들의 크기이다. 아래 코드를 보면 rapier.es.js 코드가 크게 2개가 있다. 이 경우에서 보면 @react-three/drei와 @react-three/rapier에서 각각 다른 버전의 모듈을 참고하면서 두개의 번들을 가져오면서 발생한 일이다.

npm ls @dimforge/rapier3d-compat 
koosang-vidoe-story@ C:\work\cursor\koosang-vidoe-story 
├─┬ @react-three/drei@9.122.0 
│ └─┬ maath@0.10.8 
│ └─┬ @types/three@0.176.0 
│ └── @dimforge/rapier3d-compat@0.12.0 
└─┬ @react-three/rapier@1.5.0 
	└── @dimforge/rapier3d-compat@0.14.0

해결하기 위해서는 우선 두 모듈이 서로 호환되는지 확인해야한다. @dimforge/rapier3d-compat@0.12.0 와 0.14.0 은 호환되는 타입인 것을 확인하고, packege.json에 합산 코드를 넣고 package-lock.json 삭제 후 재설치 하면 0.14.0 버전으로 합산된다. // package.json

{
	"scripts":{...},
	"overrides": {
		"@dimforge/rapier3d-compat": "0.14.0"
	},
	"dependencies":{...}