번들 사이즈 개선하기
번들 사이즈 줄이기
번들의 사이즈를 줄이기 위해서는 우선 번들의 크기를 확인해야 합니다.
yarn add -D @next/bundle-analyzer // next.config.js const withBundleAnalyzer = require("@next/bundle-analyzer")({ enabled: process.env.ANALYZE === "true", }); const nextConfig = { reactStrictMode: true, images: { domains: ["s3.us-west-2.amazonaws.com", "res.cloudinary.com"], disableStaticImages: true, }, }; module.exports = withBundleAnalyzer(nextConfig);
그리고, build
명령어를 통해 확인해보면 다음과 같이 번들 사이즈를 확인할 수 있습니다.
코드 스플릿 하기
제 블로그 중 번들사이즈가 가장 큰 요소는 refractor
과, react-syntax-highlighter
이었습니다. 그런데 refractor
은 무엇인지 모르겠어서, node module 에서 찾아보았습니다.
노드 모듈을 확인해보니 이또한 react-syntax-highlighter
이었습니다. 사실 단순히 코드라인을 이쁘게 꾸미기 위한 라이브러리였는데 용량이 가장 크다고 생각하지도 못했습니다.
next
build 명령어를 통해 출력된 메시지와, 네트워크탭을 확인해보면 app-789e
로 시작하는 파일이 402Kb
크기를 가지며 가장 큰 것을 알 수 있습니다. 번들 애널라이저에서 확인해보면, refractor
의 압축 사이즈가 202Kb
인 것을 확인해보면 사실 저의 코드나, 다른 내용보다는 해당 라이브러리의 용량이 번들 사이즈에 가장 큰 영향을 끼치고 있었습니다.
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { vscDarkPlus } from "react-syntax-highlighter/dist/cjs/styles/prism"; const CodeBlock = ({ language, code }: { language: string; code: string }) => { return ( <SyntaxHighlighter language={language} style={vscDarkPlus} PreTag='div'> {code} </SyntaxHighlighter> ); }; export default CodeBlock; // react-syntax-highlighter은 여기서만 사용됩니다.
const DynamicCodeBlock = dynamic(() => import("./CodeBlock")); // 해당 컴포넌트를 dynamic import로 불러옵니다. const MarkDown = ({ markdownString }: { markdownString: string }) => { return ( <ReactMarkdown className='prose max-w-full select-text prose-li:marker:text-black dark:prose-invert dark:prose-li:marker:text-gray lg:mt-5 lg:max-w-[75%]' components={{ code({ node, inline, className, children, ...props }) { const match = /language-(\w+)/.exec(className || ""); return !inline && match ? ( <DynamicCodeBlock code={String(children).replace(/\n$/, "")} language={match[1]} /> ) : ( <code className={className} {...props}> {children} </code> ); }, }} remarkPlugins={[slug]} > {markdownString} </ReactMarkdown> ); };
진행 후 확인해보니, 다음과 같았습니다.
위와 비교해서 보면 first load js
의 크기가 확연히 줄었음을 확인할 수 있습니다. 이전 480kB
에서 242kB
로 절반정도 줄어들었습니다.
네트워크 탭에서의 초기 로딩때에도 최상단에 보이는 _app-fa8
파일의 크기가 절반이하로 줄어든 것을 확인해 볼 수 있습니다. 코드 스플릿을 통해 해당 라이브러리가 실제로 사용되는 post/slug
페이지에서 해당 라이브러리를 다운로드 하게 될 것입니다.
아무 post/slug
페이지에 들어가보면 아래처럼 새로운 js
파일을 다운로드 받습니다.
그중, 240kB
의 파일 안에 react-syntax-highlighter
이 들어가게 됩니다.
트리 쉐이킹
위에서 분석한 내용중, nodejs.html
을 확인해 보면 다음과 같이 출력됩니다.
무언가 이상한 부분이 있습니다. 이 부분은 제가 barrel
파일을 통해서 import
를 사용하고 있었기에 생겨난 부분이었습니다. 해당 부분으로 인해, 지금은 실제로 사용하고 있지 않았음에도 번들 사이즈에 추가되고 있었습니다.
여기서 추론할 수 있는 점이, 한가지 있습니다. 사실 지금 트리쉐이킹이 잘 되고 있지 않은 게 아닐까요? 트리 쉐이킹은 사용되지 않는 코드를 제거하기 위해 JavaScript 컨텍스트에서 일반적으로 사용되는 용어입니다.
(ref: https://webpack.kr/guides/tree-shaking/ )
즉 트리쉐이킹이 잘 되었다면 사용하지 않는 코드들은 bundle
에 포함되지 않아야 합니다. 웹팩의 가이드를 보고 package.json
에 "sideEffects"
를 추가해주어야 했습니다. 일반적으로 다음과 같이 사용됩니다.
"sideEffects": false // 사용하고 있지 않은 export는 제거해도 괜찮다고 알립니다. { "name": "your-project", "sideEffects": ["./src/some-side-effectful-file.js"] } // 또는 이처럼 배열로 직접 알릴 수도 있습니다.
저는 간단히 "sideEffects": false 를 package.json
에 추가해주었습니다.
app
페이지로 접근 할때에 초기에 다운로드 되는 파일의 용량이 절반정도로 줄어들었을 확인할 수 있습니다.
다만 /post
경로에서는 위에 dynamic import로 설정한 react-syntax-highlighter
가 포함되어 용량이 조금 더 커지게 됩니다.
사용하지 않는 라이브러리 제거하기
번들 분석 결과를 세세하게 살표보니, 위의 pngjs
라이브러리가 무엇인지 궁금해졌습니다. 그리고 여기서부터 시작되는 아래까지의 라이브러리들을 합치만 꽤 큰 크기가 됩니다.
저는 gif-frames
라는 라이브러리로 gif
이미지를 base64
로 인코딩하는 유틸함수를 사용하고 있었고, 이 부분은 gif-frames
관련 부분처럼 보였습니다. 윗 부분에서 이 부분이 잡히지 않은 이유는, 제가 해당 함수를 지우지 않고 그대로 두고 있었기 때문입니다. 그런데, 저는 더 이상 해당 라이브러리를 사용하지 않고 있었습니다. 이미 모든 이미지는 webp
로 변환해 사용하고 있었기 때문입니다. 그래서 해당 라이브러리를 제거하고 사용하는 유틸함수를 제거했습니다.
관련 코드를 제거하고, 라이브러리를 제거하니, /post
페이지와 관련해 70kb
정도 용량을 줄일 수 있었습니다.
정리하기
제가 진행한 과정은 다음과 같았습니다.
- 초기 페이지에서 필요하지 않은 라이브러리를
dynamic import
로 lazy 하기. - 트리쉐이킹이 잘 되고 있는지 확인하기
- 사용하고 있지 않은 코드와 라이브러리 제거하기
이러한 과정을 통해 초기페이지 기준 480Kb
에서 절반이상으로 모듈의 사이즈를 줄일 수 있었습니다.