-
[포트폴리오] 포트폴리오 웹사이트 최적화 - 2 (next/dynamic를 사용한 code splitting)Next.js 2024. 2. 14. 22:30728x90반응형
라이트 하우스의 검사결과를 보고 번들 크기를 줄여야 겠다는 생각이 들었다
구글링을 해보니 next/analyze-bundler를 많이 사용하고 있었다
시각적으로 자바스크립트 번들 파일들을 볼 수 있어서 해당 라이브러리를 사용하기로 했다
https://nextjs.org/docs/app/building-your-application/optimizing/bundle-analyzer
Optimizing: Bundle Analyzer | Next.js
Analyze the size of your JavaScript bundles using the @next/bundle-analyzer plugin.
nextjs.org
설치
npm i @next/bundle-analyzer
# or
yarn add @next/bundle-analyzer
# or
pnpm add @next/bundle-analyzer셋팅
const withBundleAnalyzer = require('@next/bundle-analyzer')({ enabled: process.env.ANALYZE === 'true', openAnalyzer: false, }) /** @type {import('next').NextConfig} */ const nextConfig = {} module.exports = withBundleAnalyzer(nextConfig)
옵션 중 openAnalyzer: false은 필수다...
이 옵션은 빌드 후 분석 결과를 새창으로 띄우지 않게 한다이거 모르고 고통받고 있었다...
실행
ANALYZE=true npm run build
# or
ANALYZE=true yarn build
# or
ANALYZE=true pnpm buildNext.js docs에는 위와 같이 써있었으나...
대문자 치기 귀찮고 나중에 까먹을 거 같아서 .env 파일에 환경변수로 넣어줬다
ANALYZE=true
이렇게 하면 그냥 npm run build를 하면 자동으로 된다
빌드를 하고 나면 .next 파일이 생성된다
그 안의 analyze 폴더 안에 client.html이라는 파일로 분석 결과가 저장되어있다
해당 html 파일을 열어보면 아래와 같이 시각적으로 보기 편하게 나와있는 것을 볼 수 있다
(참고로 번들 파일의 이름은 번들링할 때마다 조금씩 달라진다 !)
왼쪽부터 전체 번들 파일들 - chunk/app.js 의 ui 파일 - chunk/app.js 의 ui/Toast 컴포넌트 파일 일단 살펴보니 Contact 폴더에 있던 Toast 컴포넌트는 처음부터 받아올 필요가 없기 때문에
code splitting을 통해 지연로딩을 시키기로 했다
리액트만 사용했다면 React.lazy()와 Suspense를 사용해 했겠지만 Next에서는 dynamic이라는 통해 dynamic import를 지원해주고 있다
공홈 설명을 보니 React.lazy()와 Suspense를 합쳐서 지연 로딩을 좀 더 편하게 할 수 있게 해주고 있다고 한다
https://nextjs.org/docs/pages/building-your-application/optimizing/lazy-loading
Optimizing: Lazy Loading | Next.js
Lazy load imported libraries and React Components to improve your application's loading performance.
nextjs.org
리액트에서의 사용법은 아래와 같다면
import React, { Suspense } from 'react' const AnyComponent = React.lazy(() => import('./AnyComponent')) function Component() { return ( <div> <Suspense fallback={<div>로딩</div>}> <AnyComponent /> </Suspense> </div> )}
Next.js에서 제공하는 dynamic을 사용하면 아래와 같다
import dynamic from 'next/dynamic' const AnyComponent = dynamic(() => import('./AnyComponent'), { loading: () => <div>로딩중</div> }) function Component() { return ( <div> <AnyComponent /> </div> )}
사실 토스트 창을 React의 ContextAPI를 사용해서 만들었었는데
다른 컴포넌트에서 전체적으로 사용할 일이 없어서 굳이 ContextAPI로 만들 필요가 없었다
(사실 Context API 연습 겸 사용했음)
그래서 ToastContext를 삭제하고 Toast 컴포넌트만 사용해서 나중에 로드되도록 했다
const Contact = () => { const ToastComponent = dynamic(() => import("../components/ui/Toast"), { loading: () => <div>로딩</div>, }); return ( <section> // 중략 {toast && <ToastComponent onClick={() => setToast(false)} />} </section> ) }
ui에 Toast 컴포넌트가 사라지고 parsed size도 65.23KB -> 56.98KB로 낮아졌다
dynamic import를 통해 초기 로드시 다운로드 할 번들 파일 용량을 줄였다
(처음 받는 번들 파일 용량만 줄어든거지 전체 용량은 같음)
그리고 보다보니 모달도 처음부터 로드될 필요가 없었다
내가 만든 모달은 Context API를 사용해서 만들었는데 이를 사용한 이유는 위의 Toast 와 같이 Context API를 한 번도 사용해 본적이 없었기 때문에 연습 겸 사용해서 만든 것이었다
사실 모달이 프로젝트 내에서 전체적으로 공유해야할 상태를 가진 것도 아니고, 지금 모달이 Project 컴포넌트에서 사용되지만 만약 다른 곳에서도 모달이 쓰이게 된다면? 모달의 재사용성을 위해서라도 커스텀훅으로 빼는 게 낫긴 하다. 그리고 해당 Context API를 구독하고 있는 모든 컴포넌트들이 리렌더링되기 때문에 불필요한 렌더링이 발생할 수도 있다
그래서 Context API를 없애고 커스텀훅으로 모달을 분리한 후 dynamic import를 적용했다...!
const { showModal, openModalHandler, closeModalHandler } = useModal(); const DynamicModal = dynamic(() => import("../components/modal/Modal")); // 중략 return ( <section> // 중략 {showModal && ( <ModalPortal> <DynamicModal closeModalHandler={closeModalHandler} /> </ModalPortal> )} <ScrollButton pageName="contact" /> </section> </section> )
적용 전 - 적용 후 용량이 많이 줄었다 ㅎ
반응형'Next.js' 카테고리의 다른 글
[포트폴리오] 포트폴리오 웹사이트 최적화 - 1 (Properly size images, Serve images in next-gen format) (2) 2024.02.07 [포트폴리오] 포트폴리오 웹사이트 최적화 - 1 (Light House 및 수정할 부분) (2) 2024.01.30