개발 기술/개발 이야기

[JS/Module] CommonJS와 ES Modules는 무엇일까?

by GicoMomg 2022. 11. 7.

1. CommonJS, ES Modules는 무엇일까?

  • 우리는 JS 모듈을 내보내거나 가져올 때 2가지 방식을 사용한다.
  • 첫번째 방법은 module.exports로 모듈을 내보내고 require()로 접근하는 CJS(CommonJS),
  • 두번째 방법은 export로 모듈을 내보내고 import로 접근하는 ESM(ES Modules)이 있다.
// CJS 방법
module.exports = { ... }        // 모듈 내보낼 때

const utils = require('utils'); // 모듈 가져올 때   
// ESM 방법
export.default =()=> { ... }; // 모듈 내보낼 때

import utils from 'utils';    // 모듈 가져올 때  

  • 그런데 ESM, CJS는 같은 기능을 하는 것 같은데, 어떤 차이가 있는걸까?
  • 이번시간에는 ESM, CJS의 차이점에 대해 간단히 알아보았다 🙂 (출처)



2. CJS, MJS의 차이점

1) CJS 방식

(1) CJS는…

📌 require로 모듈에 접근하고 module.exports로 모듈을 내보내는 ESM 방식

  • NodeJS에서 지원하는 모듈 방식으로, 초기 Node버전부터 사용되었다.
  • 별도의 설정이 없다면 CJS가 기본값이다.

(2) 모듈에 접근할 때, require()

  • 외부 모듈에 접근할 때는 require()을 사용한다.
const utils = require('utils');  // utils 모듈에 접근하기

(3) 모듈을 내보낼 때, module.exports

  • module.exports로 모듈을 내보낸다.
  • 모듈을 내보내는 방식은 named exports, default exports 두가지가 있다.
module.exports.utils = { ... };  // named exports
module.exports = { ... }         // default exports

  • named exports의 경우 export 대상을 명명하여 내보내는 방식이다.
  • 한 파일에서 여러 개의 변수, 클래스, 함수를 내보낼 때 주로 사용한다.
// calculator.js

module.exports.add = (a, b) => a + b;
module.exports.sub = (a, b) => a - b;

  • named exports 모듈은 2가지 방식으로 접근할 수 있다.
const calculator = require("./calculator.js");  
const { add } = require("./calculator.js");    

console.log(calculator.add(2, 2)); // 출력: 4
console.log(add(2, 2));            // 출력: 4

  • default exports의 경우, 별도의 명명없이 내보내는 방식이다.
  • 한 파일에서 하나의 변수, 클래스, 함수를 내보낼 때 쓰인다.
// calculator2.js

module.exports = (x, y) => x + y

  • default exports 모듈을 접근할 때는 원하는 이름으로 설정하여 사용할 수 있다.
const add = require("./calculator2.js"); // 모듈을 add로 명명하기

console.log(add(2, 2));                 // 출력: 4

(4) CJS의 특징

  • require()는 즉시 스크립트를 실행하는 구조이다.
  • top-level await가 불가능하므로 동기적으로 작동한다.
  • 동기로 작동하므로 promise를 리턴하지 않고, module.exports에 설정된 값만을 리턴한다.
  • import 순서에 따라 스크립트를 실행한다.



2) ESM 방식

(1) ESM은…

📌 import로 모듈에 접근하고 export로 모듈을 내보내는 ESM 방식

  • ES Modules(MJS)는 ECMAScript에서 지원하는 방식이다.
  • Node14에선 CJS, MJS이 공존하는데, 두 개를 동시에 사용하기 위해 별도의 처리가 필요하다.
  • 모듈 시스템을 CJS(기본값)에서 ESM으로 변경할 시, JS 일부 동작이 변경된다. (호환성 문제)

(2) ESM 사용하는 법

  • ESM 방식을 사용하기 위해선 package.json에 “type”: “module”을 설정해야 한다.
// package.json

{
  "type": "module",
}

(3) 모듈에 접근할 때, import()

  • 모듈에 접근하기 위해서 import을 사용한다.
import utils from 'utils';
import { add } from 'utils';
import { add as add_func } from "utils";

(4) 모듈을 내보낼 때, export

  • export로 내보낸 모듈은 import로 접근할 수 있다.
  • named exportsdefault exports를 두가지를 지원한다.
  • 아래는 named exports의 예시이다.
// calculator.js

export const  = (x, y) => x + y;

  • named exports는 명명된 이름으로만 모듈을 불러올 수 있다.
  • as를 사용해 명명된 이름을 다른 별칭으로 수정할 수 있다. ( 아래 예시: sumsum_func )
import { sum } from "./calculator.js";
import { sum as sum_func } from "./calculator.js";  // 다른 별칭으로 수정

console.log(sum(2, 4));      // 출력: 6
console.log(sum_func(2, 4)); // 출력: 6

  • 만약 default 방식으로 접근할 시 SyntaxError가 발생한다.
// SyntaxError does not provide an export named 'default'
import calculator from "./calculator.js";

console.log(calculator.sum(2, 4));  // error

  • 다음은 default export의 예시다. CJS와 마찬가지로, 별도로 이름을 지정하지 않는다.
// calculator2.js

export default (x, y) => x + y;

  • 명명된 이름이 없으므로 원하는 이름으로 import하면 된다.
import add from "./calculator2.js";

console.log(add(2, 4)); // 출력: 6

(4) ESM의 특징

  • top-level await를 지원하므로 module loader가 비동기 환경에서 실행된다.
  • 그러므로 CJS처럼 스크립트를 바로 실행하지 않고 import, export구문을 찾아 스크립트를 파싱한다.
  • 파싱 단계에서 import, export 에러를 감지할 수 있다.
  • 모듈을 병렬로 다운로드하지만, 실행은 순차적으로 한다.
  • importexport를 지원하지 않는 브라우저가 있기에, ESM 사용을 위해 번들러가 필요하다.




이번 포스팅에선 CJS, ESM에 대해 알아보았다. 먼저 CJSrequire로 모듈에 접근하고 module.exports로 모듈을 내보내며, ESMimport로 모듈에 접근하고 export로 모듈을 내보낸다는 걸 알았다. 또한, 모듈은 기본적으로 CJS 방식으로 동작하므로 ESM을 사용하기 위해선 package.json 설정과 추가적인 번들러 설정이 필요하다는 것도 알 수 있었다. :)

반응형

댓글