최근에 Next 공식 문서에서 우연히 보게 된 패키지 import 최적화에 대해 소개해 보겠습니다. 이 Vercel 글은 블로그는 패키지 최적화를 어떻게 진행했는지에 대해 설명하고 있습니다. 이 글을 기반으로 블로그를 작성해 보려고 합니다.
밑의 블로그는 위의 글을 번역해 주신 글입니다!🙇♀️
첫 번째로 배럴 파일에 대해 설명해 보겠습니다.
배럴(Barrel) 파일이란?
JavaScript에서 하나의 파일에서 여러 모듈을 그룹화해 내보내는 방법입니다.
이를 사용하면, 그룹화된 모듈에 액세스 할 수 있는 중앙화된 위치를 제공해 그룹화된 모듈을 더 쉽게 가져올 수 있습니다.
다음과 같은 폴더가 있을 때,
📂project
┣ 📜module1.js
┣ 📜module1.js
┗ 📜module1.js
배럴 파일이 없으면, 모듈을 개별적으로 가져와야 합니다.
배럴 파일이 없을 때
import module1 from "./project/module1";
import module2 from "./project/module2";
import module3 from "./project/module3";
배럴 파일이 있으면, 내부 구조에 대해 알 필요 없이 모든 모듈을 일괄적으로 가져올 수 있습니다.
배럴 파일 (index.js) 생성
export { default as module1 } from "./module1"; // or export * from "./module1";
export { default as module2 } from "./module2";
export { default as module3 } from "./module3";
📂project
┣ 📜module1.js
┣ 📜module1.js
┣ 📜module1.js
┗ 📜index.js // 배럴 파일
배럴 파일이 있을 때
import { module1, module2, module3 } from './project';
배럴 파일 사용의 이점
- 관련 모듈에 쉽게 접근할 수 있어 코드 구성과 유지 보수성이 향상됩니다.
- 모듈이 많은 대규모 프로젝트에서 import 프로세스를 구성하고 단순화하는데 유용합니다.
배럴 파일 사용의 문제점
- 많은 항목들을 가져오는 배럴 파일에서 단일 내보내기를 사용할 경우, 불필요한 다른 모듈을 가져오기 때문에 사용하는 모듈 외에 추가 파일을 처리해야 하는 문제가 발생해 성능 저하가 발생할 수 있습니다.
- import 프로세스에 또 다른 간접 계층을 추가하므로 특정 값의 출처를 이해하기 어려워질 수 있어 복잡성이 증가합니다.
다음으로는 일반적으로 패키지 최적화를 수행할 경우 사용하는 방법인 트리 셰이킹(Tree-shake)에 대해 간단히 설명하고, 왜 Next에서는 이 방법을 사용하지 않았았는지에 대해서 다루어보겠습니다.
트리 쉐이킹(Tree-shake)을 사용하지 않은 이유
Tree Shaking은 간단히 말해서 번들에서 사용되지 않는 코드를 제거하여 패키지 크기를 최적화하는 기술입니다. 이는 webpack, rollup, esbuild 등과 같은 Bundler의 기능입니다. Bundler는 런타임에 종속성이 필요하기 때문에 이 라이브러리 내부에서 최적화를 수행할 수 없습니다. 모든 모듈을 컴파일하고 분석해야 하므로 런타임 종속성이 있는 라이브러리의 경우 복잡하고 느리기 때문에, Next.js는 트리 쉐이킹을 하지 않는 다른 방법을 선택했습니다.
첫 번째 시도: modularizeImports
modularizeImports 옵션을 사용하면 내보낸 이름과 패키지의 배럴 파일 뒤에 있는 원래 모듈 경로의 매핑 관계를 구성할 수 있습니다. 이 말은 즉, 두 번째 코드처럼 배럴 파일을 건너뛰고 대상에서 직접 import 하여 불필요한 모듈을 로드하지 않도록 할 수 있습니다. 이러한 변경으로 인해 빌드 시간과 런타임이 모두 빨라졌다고 설명하고 있습니다. (배럴 파일 설명의 폴더 구조 참고)
import { module2 } from './project';
import module2 from "./project/module2";
하지만 이 방식에는 치명적인 단점이 있습니다. 라이브러리의 내부 디렉터리 구조를 기반으로 해, 대부분 수작업으로 구성되기 때문에 효율적으로 확장할 수 있는 방법이 없고, 라이브러리의 내부 구조가 변경되면 무효화가 됩니다.
해결책: optimizePackageImports
이 방식은 modularizeImports 옵션 구성의 어려움을 해결하기 위해 도입되었다고 합니다.
next.config.js
module.exports = {
experimental: {
optimizePackageImports: ['package-name'], // 패키지 이름 작성
},
}
이렇게 옵션을 활성화하면 Next.js가 해당 패키지의 진입 파일을 분석하고 배럴 파일인 경우, moduleizeImports 작동 방식과 유사하게 즉시 파일을 분석하고 모든 import를 자동으로 매핑합니다.
이 방법은 한 번에 항목 배럴 파일만 확인해 트리 쉐이킹보다 비용이 저렴하고, 중첩된 배럴 파일과 와일드카드 내보내기(export
* from)를 재귀적으로 처리하고, 배럴이 아닌 파일에 부딪치면 프로세스를 종료합니다.
또한 이 옵션은 패키지 내부 구현에 의존하지 않습니다.
성능 개선 측정
위의 이미지에서 볼 수 있듯이 아이콘 또는 컴포넌트 라이브러리 중 하나를 사용할 경우, 라이브러리에 따라 개발 시간이 15% ~ 70% 단축된다고 합니다. 프로덕션 빌드 시에도 약 28% 더 빠르게 실행된다고 합니다!
자세한 사항은 블로그 내용을 참고하시면 좋을 것 같습니다!!
'FE Study > Next.js' 카테고리의 다른 글
원티드 프리온보딩 FE 챌린지 7월 - CSR / SSR with Next.js (0) | 2023.06.29 |
---|