Anonymous View

ECMAScript Modules

ECMAScript 모듈(ESM)은 웹에서 모듈을 사용하기 위한 사양입니다. 모든 최신 브라우저와 권장하는 웹 모듈 코드 작성법에서 지원됩니다.

Webpack은 ECMAScript 모듈을 최적화하기 위한 처리를 지원합니다.

Exporting

export 키워드를 사용하면 ESM 항목을 다른 모듈에 노출할 수 있습니다.

export const CONSTANT = 42;

export let variable = 42;
// 오직 읽는 값만 노출됩니다.
// 외부에서 변수를 수정할 수 없습니다.

export function fun() {
  console.log("fun");
}

export class C extends Super {
  method() {
    console.log("method");
  }
}

let a, b, other;
export { a, b, other as c };

export default 1 + 2 + 3 + more();

Importing

import 키워드를 사용하면 다른 모듈에 대한 참조를 ESM으로 가져올 수 있습니다.

// 다른 모듈에 export하기 위해 "bindings"를 가져옵니다.
// 바인딩은 활성상태입니다. 값은 복사되지 않습니다.
// 대신 "변수"에 접근하면 현재 값을 얻습니다.
// 가져온 모듈에서
import { CONSTANT, variable } from "./module.js";

// "기본" export를 가져오는 단축입니다.
import theDefaultValue from "./module.js";

// 모든 export를 가지는 "네임스페이스 객체"를 가져옵니다.
import * as module from "./module.js";

module.fun();

When importing a namespace object from an ECMAScript Module, webpack follows the ESM convention of setting Symbol.toStringTag to "Module" on the namespace object.

Flagging modules as ESM

기본적으로 webpack은 파일이 ESM인지 또는 다른 모듈 시스템인지 자동으로 감지합니다.

Node.js는 package.json의 속성을 사용하여 파일의 모듈 유형을 명시적으로 설정하는 방법을 확립했습니다. package.json에서 "type": "module"을 설정하면 package.json 아래의 모든 파일이 ECMAScript 모듈이 됩니다. "type": "commonjs"를 설정하면 CommonJS 모듈이 됩니다.

{
  "type": "module"
}

또한, 파일은 .mjs 또는 .cjs 확장자를 사용해서 모듈 유형을 설정할 수 있습니다. .mjs는 ESM이 되도록 강제하고, .cjs는 CommonJS가 되도록 강제합니다.

DataURIs에서 text/javascript 또는 application/javascript mime 유형을 사용하면 모듈 유형도 ESM으로 강제로 적용됩니다.

모듈 형식뿐 아니라 모듈 플래그를 ESM으로 지정하는 것은 로직 해석, interop 로직 및 모듈에서 사용 가능한 심볼에 영향을 줍니다.

import.meta in ESM

Webpack은 ESM에서 사용할 수 있는 여러 import.meta 속성을 제공합니다.

속성설명
import.meta.url현재 모듈 파일의 URL로, new Worker()new URL()에 사용할 수 있습니다.
import.meta.webpackwebpack 메이저 버전 번호입니다. (예: 5)
import.meta.webpackHotmodule.hot와 동일한 기능으로, ESM에서 HMR에 사용할 수 있습니다.
import.meta.webpackContextrequire.context의 ESM 대응 API

예시 - 에셋에 import.meta.url 사용하기:

// 현재 모듈을 기준으로 같은 디렉터리의 파일 경로를 해석합니다.
const iconUrl = new URL("./icon.png", import.meta.url);
const img = document.createElement("img");
img.src = iconUrl.href;

예시 - ESM에서 HMR 사용하기:

if (import.meta.webpackHot) {
  import.meta.webpackHot.accept("./module.js", () => {
    // 업데이트를 처리합니다.
  });
}

Top-Level Await

ESM에서는 모듈 최상위 레벨에서 await를 사용할 수 있습니다. Webpack은 해당 모듈을 자동으로 비동기 모듈로 처리합니다. 이 기능은 5.83.0부터 기본 활성화되었고, experiments.topLevelAwait 옵션 자체는 5.102.0에서 제거되었습니다. 즉, 별도 설정 없이 동작합니다.

// user.js (비동기 ESM 모듈)
const response = await fetch("/api/user");

export const user = await response.json();
// index.js - 비동기 모듈 import도 기대한 대로 동작합니다.
import { user } from "./user.js";

console.log(user.name);

Fully Specified Imports

ESM의 import는 더 엄격하게 해석됩니다. 파일이 ESM으로 지정된 경우, Node.js 규칙에 따라 상대 경로 요청에는 파일 확장자(예: *.js, *.mjs)를 포함해야 합니다.

// 실패합니다. 확장자가 없습니다.
import { helper as missingExt } from "./utils";

// ESM에서 올바른 방식입니다.
import { helper } from "./utils.js";

대규모 CJS 코드베이스를 마이그레이션할 때처럼 이 검사를 비활성화해야 한다면 fullySpecified=false를 사용할 수 있습니다.

// webpack.config.js
export default {
  module: {
    rules: [
      {
        test: /\.m?js/,
        resolve: {
          fullySpecified: false,
        },
      },
    ],
  },
};

CommonJS Interop

ESM에서는 require, module, exports, __filename, __dirname 같은 CommonJS 구문을 사용할 수 없습니다.

ESM에서 CommonJS 모듈을 import할 때는 default export만 사용할 수 있으며, 이는 module.exports 객체 전체를 가리킵니다.

// esm-consumer.js (ESM)
import cjs from "./cjs-module.js";
// CJS에서는 named import가 동작하지 않습니다.
import { foo } from "./cjs-module.js"; // undefined

// cjs-module.js (CommonJS)
module.exports = { foo: 1, bar: 2 };

console.log(cjs.foo); // 동작합니다. cjs는 exports 객체 전체입니다.

이 엄격한 동작은 webpack이 import된 모듈을 CommonJS로 판단할 때 적용됩니다. 해당 모듈이 자체적으로 ESM export 구문을 사용하면 webpack이 이를 자동으로 ESM으로 감지하므로 named import도 정상적으로 동작합니다. 이 문제는 "type": "module"이 설정된 프로젝트에서 .js 파일을 혼용할 때 자주 나타납니다. 프로젝트 내부 일부 파일은 ESM으로 처리되는 반면, node_modules의 서드파티 패키지는 여전히 CommonJS인 경우가 많기 때문입니다.

Common Migration Errors

ReferenceError: require is not defined

파일이 ESM으로 처리되면 CommonJS 전역 객체인 require, module, exports, __filename, __dirname를 사용할 수 없습니다.

해결: require()import 구문으로 바꾸세요. 조건부 또는 동적 로딩이 필요하다면 import()를 사용하세요.


Must use import to load ES Module (Node.js) / SyntaxError: Cannot use import statement in a module (browser)

이 오류는 ESM import/export 구문을 사용하는 파일이 ESM으로 지정되지 않았을 때 발생합니다. package.json"type": "module"이 없거나, 파일 확장자가 .mjs가 아니라 .js인 경우가 대표적입니다.

해결: package.json"type": "module"을 추가하거나 파일 확장자를 .mjs로 변경하세요.


Module not found: Error: Can't resolve './utils' (missing extension)

ESM에서는 상대 import에 파일 확장자를 포함해야 합니다. Webpack도 여기서는 Node.js의 ESM 규칙을 따릅니다.

해결: import { helper } from './utils'import { helper } from './utils.js'로 바꾸거나, 마이그레이션하는 동안에는 webpack 설정에서 fullySpecified: false를 지정해 이 검사를 끌 수 있습니다.

Edit this page·
« Previous
Lazy Loading
Next »
Shimming

2 Contributors

sokraryzrr

Translators