이번 글은 Default vs Named Exports: Which is Better for Tree-Shaking?를 참고하였습니다.
해당 글은 번들러가 Treeshaking을 할 때 유명 내보내기인 export가 효율적일지 기본 내보내기인 default export가 효율적일지에 대한 내용입니다.
mdn-export
mdn측에서 export default = 기본 내보내기, export = 유명 내보내기로 번역해서 해당 네이밍을 따르도록 하겠습니다.
vscode
에 대한 내용이 들어있습니다.
최신 JavaScript 애플리케이션에서는 모듈 번들러(예: webpack 또는 Rollup)를 사용하여 여러 JavaScript 파일을 단일 파일로 번들링할 때, 죽은 코드를 자동으로 제거합니다. 이는 프로덕션에 바로 사용할 수 있는 코드를 준비하는 데 중요한 작업으로, 깔끔한 구조와 최소한의 파일 크기로 코드를 최적화하는데 도움이 됩니다.
예를 들어 우리가 React-hook-form이라는 라이브러리를 사용한다고 할 때, 그 안에 있는 모든 라이브러리를 사용하지 않습니다.
사용하는 어떠한 모듈들만 import해서 사용하기 마련입니다.
프로젝트 내에 모듈들의 모음인 라이브러리 설치 되어 있지만, 사용하는 모듈만 번들링 하는 기술, 그것이 tree shaking입니다.
Default vs Named Exports: Which is Better for Tree-Shaking?에서 가정하는 export default는 아래와 같은 상황입니다.
//my-librrary.js
function functionA(){return console.log("I'm A")}
function functionB(){return console.log("I'm B")}
function functionC(){return console.log("I'm C")}
var library={functionA,functionB,functionC}
export default library;
// 모듈에서 전체 모듈을 가져옴
import library from 'my-library';
// 하지만 실제로는 functionA만 사용
myLibrary.functionA();
esbuild 번들링 툴로 번들링을 한다면, 아래와 같은 결과를 얻을 수 있습니다.
이런 경우에 MyLibrary내에 존재하는 모든 functionA, functionB, functionC를 전부 번들링하니 메모리, 시간측에서 손해를 본다는 내용입니다.
//my-librrary.js
export function functionA(){return console.log("I'm A")}
export function functionA(){return console.log("I'm B")}
export function functionA(){return console.log("I'm C")}
// 모듈에서 여러 함수를 가져옴
import { functionA, functionB, functionC } from 'my-library';
// 하지만 실제로는 functionA만 사용
functionA();
esbuild 번들링 툴로 번들링을 한다면, 아래와 같은 결과를 얻을 수 있습니다.
위와 같은 경우에 tree shaking을 지원하는 번들러는 funtionA 모듈만 번들링하여 프로덕트에 포함되는 번들의 크기를 줄이고, 안정성을 높이게됩니다.
하지만 React 개발자들은 export default를 어느 상황이 사용할까요?
예를 들어, HelloWorld.tsx라는 컴포넌트를 개발한다고 생각해봅시다.
const Hello=()=>{
return <span>Hello </span>
}
const World=()=>{
return <span>World!</span>
}
const HelloWorld=()=>{
return <>
<Hello />
<World />
</>
}
이는 Hello world!를 출력하고, 우리는 Hello와 World는 재사용을 하지 않아 관심사에서 없다고 가정하겠습니다.
밖에선 HellowWorld 컴포넌트만 출력하기 때문이죠.
이에 저는 기본 내보니기인 export default HelloWorld;
를 통해 밖에서 import할 수 있도록 기본 내보내기를 했었습니다.
위 상황에 대해서는 어차피 이 안에서 쓰이는 함수이고, tree shaking을 할 이유도 없기 때문에 당연히 써도 좋을 것 같다고 느낍니다.
import함에 있어서 {}도 안써도 되고, 너무나도 이쁜 코드가 탄생합니다.
하지만 이는 vscode에서 악영향을 미칩니다.
auto import를 위한 IntelliSense와, 개발 - 컴파일 단계에서 사용하는 TypeScript는 정적
으로 코드를 분석합니다
정적 분석은 코드를 실행하지 않고 소스 코드만 보고 분석하는 것을 말합니다.
jsx// HomePage.jsx
const Home = () => <div>홈페이지</div>;
export default Home;
VS Code는 이 파일에서 아래와 같은 정보를 추출합니다.
이 파일에는 기본 내보내기가 있구나, 그 내보내기의 값은
Home
이라는 변수를 참조하는구나
하지만 기본 내보기는 불러올 때 어떤 이름으로든 가져올 수 있다는 문제가 있습니다.
import Home from './HomePage'; // <Home />
import HomePage from './HomePage';// <HomePage />
import AnythingIWant from './HomePage';// <AnythingIWant />
// 모든 이름으로 사용 가능하다.
이 유연성 때문에 VS Code는 가장 좋은 추측을 할 수밖에 없고, 그래서 흔히 파일 이름을 기본값으로 사용합니다.
이 과정에서 추측을 해야하는 overhead가 발생합니다.
또한 모듈을 먼저 사용하고, command + . 혹은 자동 추천에서 import 할 때 부정확한 모듈을 import할 가능성이 생기게 됩니다.
또한 정해진 이름이 없이 사용하는 곳 마다 다른 이름으로 import를 하게 된다면, 그에 따른 유지보수에 대한 부담도 증가하게 됩니다.
반면 유명 내보내기를 사용하면
jsx// HomePage.jsx
export const Home = () => <div>홈페이지</div>;
VS code는 이 파일에서 아래와 같은 정보를 추출합니다.
이 파일에는 Home이라는 이름으로 내보내진 상수가 있구나, 다른 곳에서 가져와도 이 파일을 가져가야 하구나
// 유효한 임포트 방식
import { Home } from './HomePage';
import { Home as MyHome } from './HomePage';
그래서 가져오기를 할 때,VS Code는 정확한 이름을 제안할 수 있습니다.
또한 Home이라는 컴포넌트나 변수를 가져온 후 이름을 바꾸는 것을 명시적으로 표시할 수 있습니다.
React 개발에서 기본 내보내기와 유명 내보내기중 어느 것을 선택해야 하는지에 대한 결론은 다음과 같습니다
유명 내보내기가 트리 쉐이킹에 더 효율적입니다.
기본 내보내기로 객체를 내보내면 사용하지 않는 함수들까지 번들에 포함되어 효율성이 떨어집니다.
유명 내보내기는 VS Code의 자동 완성과 IntelliSense에 더 정확한 정보를 제공합니다.
기본 내보내기는 임포트 시 이름을 자유롭게 지정할 수 있어 VS Code가 추측해야 하는 오버헤드가 발생합니다.
SFC(Single-File Component) 원칙을 따를 때, 파일 내에서 주요 컴포넌트만 내보내는 경우 기본 내보내기를 사용하는 것이 코드가 간결해 보일 수 있습니다. 하지만 안전성과 유지보수성 면에서 유명 내보내기가 장기적으로 좋습니다.
저는 위와 같은 상황을 공부하고, 알아보면서 기본 내보내기에 대한 사용을 지양하기로 결정했습니다.
하지만 그보다 중요한건 자신에게 처한 개발 환경과 팀원과의 코드 컨벤션입니다.
최대한 효율적으로 코드를 작성하되, 팀원분들에게 어필하여 내보내기 문화를 바꿔 보는건 어떨까요?