13. ES Module, Dynamic Import, Top Level await

13-1. ES Module

CommonJS 문법이 표준 문법이라고 생각할 수도 있는데, 사실 표준은 아니다.
자바스크립트 자체에 표준이 없었기 때문에, CommonJS라는 자체적인 표준을 정해서 쓰고 있었던 것이다.

그런데 ES Module이 표준으로 정해지면서 ES Module 문법을 많이 사용하고 있다.
아직 100% ES Module만을 사용하기엔 한계가 있지만, 장기적으로 봤을 때, CommonJS에서 ES Module로 대체가될 것이다.

확장자는 mjs를 쓴다.
CommonJS는 확장자를 cjs를 쓸 수 있다.
그런데 그렇게 안하는 이유는 js 확장자로해도 cjs가 디폴트이기 때문이다.

CommonJSrequire, module.exports, exports 이런건 함수 및 객체이다.
사용자가 마음만 먹으면 다른 값으로 변형할 수도 있다.

하지만 ES6 Module 문법은 예약어이다.
사용자가 마음대로 바꿀 수 없다.

13-1-1. package.json type: module

mjs 확장자를 사용하기 싫다면, package.json"type": "module"을 넣어주자.
그러면 js 파일도 ES Module 파일로 인식을 한다.
import할 때, js, mjs 이런 확장자는 생략할 수 없다.
require로 할 땐, 생략 가능했다.

13-1-2. 확장자 생략

노드 창시자가 편의상 확장자 및 index.js 이런 파일명 생략을 가능하게 했는데, 창시자가 직접 이렇게 만든걸 후회한다고 했다.
그래서 ES Module 부터는 확장자 및 파일명을 정확히 명시해줘야된다.

13-1-3. __filename, __dirname, import.meta.url

ES 모듈에서는 __filename, __dirname, require, module.exports, exports 사용 불가능하다.
__filename 대신 import.meta.url, __dirnameimport.meta.url에서 추출하면 된다.

CommonJS에서 ES Module 호출 가능하다.
ES Module에서도 CommonJS 호출 가능하다. (안되는 케이스들도 좀 있음)

13-2. Dynamic Import

CommonJS 모듈에서는 다이나믹 임포트가 되는데, ES Module에서는 다이나믹 임포트가 안된다.

const a = false;
if (a) {
  // 다이나믹 임포트는 이렇게 if 문같은거에 넣는걸 말한다.
  // 즉, a가 false 이므로 여기는 실행될 일이 없다.
  require('./func.mjs');
}
console.log('성공');
const a = false;
if (a) {
    // 그런데 이런건 안된다.
    // ES Module에선 import는 반드시 최 상단에 위치해야된다.
    import './func.mjs';
}
console.log('성공');

그럼 ES Module에서는 다이나믹 임포트가 불가능한가?
그건 또 아니다.
다이나믹 임포트를 위해서 새로운 문법이 준비가 되어있다.

const a = true;
if (a) {
  // import는 promise이기 때문에 await를 써줘야된다.
  const a1 = await import('./func.mjs');
  console.log(a1);
  const a2 = await import('./var.mjs');
  console.log(a2);
}