안녕하세요. 오늘은 ES Module에 대해 글을 작성해보려고 합니다.
해당 내용은 NextJs의 공식문서의 내용 중 Edge runtime에 대해 읽다가 ES Module이 정확하게 무엇인지 인지하지 못하는 저를 발견하였습니다.
그래서 이를 글로 정리하면서 익히고자 작성하게 되었습니다.
JS의 표준 모듈 시스템.
JS 파일을 여러 개로 나누고, 필요한 코드만 가져와 사용할 수 있도록 만들어진 공식 방식.
Es Module에서는 다른 파일에서 사용할 기능을 export로 내보내고, 필요한 곳에서 import를 통해 가져옵니다.
// math.js
export function add(a, b) {
return a + b;
}
// app.js
import { add } from "./math.js";
console.log(add(1, 2));
Es Module이 등장하기 전에 JS에는 CommonJS(Node.js)와 같은 모듈 시스템이 존재했습니다.
const math = require("./math");
module.exports = { add };
하지만 이러한 방식은 JS의 공식 표준이 아니었고, 브라우저에서도 사용할 수 없었습니다.
더불어 CommonJS는 모듈을 실행 시점에 불러오기 때문에 번들러가 프로젝트의 의존성을 미리 분석하기 어렵다는 단점이 있었습니다.
이러한 문제를 해결하기 위해 JS 표준인 ECMAScript에서 ES Module을 도입하게 되었습니다.
ESM에서는 필요한 기능을 export하고 다른 파일에서 import하여 사용할 수 있습니다.
// user.js
export const name = "Camter";
export const age = 20;
import { name, age } from "./user.js";
Es Module의 가장 중요한 특징은 정적 분석이 가능하다는 것입니다.
import { add } from "./math.js";
다음과 같은 코드에서, JS 엔진이나 번들러는 코드를 실행하기 전에
를 모두 파악할 수 있습니다.
반면 CommonJs는 사전에 분석이 어렵다.
정적 분석이 가능하기 때문에 사용하지 않는 코드도 쉽게 제거할 수 있습니다.
// math.js
export function add() {}
export function subtract() {}
export function multiply() {}
export function divide() {}
import { add } from "./math.js";
위에서 실제 사용하는 함수는 add 뿐이다.
ESM을 사용하는 번들러(Webpack, Vite, Turbopack)는 이를 분석하여
를 최종 번들에서 제거한다.
이를 Tree Shaking이라고 하며 최종 JS 파일의 크기를 줄여 애플리케이션의 성능을 향상시킵니다.
ESM은 필요한 시점에만 모듈을 불러오는 것도 가능합니다.
const module = await import("./chart.js");
이처럼 import() 사용하면 필요한 순간에만 모듈을 다운로드하여 사용할 수 있습니다.
이를 기반으로 React의 lazy() 및 NextJs의 Dynamic Import 등이 동작하며, 초기 로딩 속도를 개선하는데 활용됩니다.
ESM(Es Module)은 JS의 공식 모듈 시스템으로 코드를 기능별로 분리하고 재사용할 수 있게 해줍니다.
하지만 진정한 가치는 단순히 파일을 나누는 것이 아니라 정적 분석이 가능하다는 점에 있습니다.
정적 분석을 통해 번들러는 프로젝트의 의존성을 미리 파악하고 Tree Shaking이나 Code Splitting 같은 최적화를 수행할 수 있습니다. 이러한 이유로 ES Module은 현재 대부분의 JS 및 프론트엔드 프레임워크에서 표준으로 사용되고 있으며 현대 웹 개발의 핵심 기반 기술로 자리 잡고 있습니다.