1. 탐구
1. CommonJS Module
1. CommonJS
- CommonJS : JS를 브라우저에서뿐 아니라 서버사이드 애플리케이션이나 데스크톱 애플리케이션에서도 사용하려고 조직한 자발적 워킹 그룹
Common: JS를 브라우저에서만 사용하는 언어가 아닌 일반적인 범용 언어로 사용할 수 있겠다는 의지
Module명세1.0,1.1,1.1.1등 존재Node.js모듈도 Module 명세 1.0 따름
- CommonJS 모듈 : NodeJs에서 자바스크립트 패키지를 불러올 때 사용하는 근본있는 방식
- Node.js는 browser보다 빨리 모듈화를 위한 대책을 보유하고 있었음 : CommonJS, AMD, Webpack-babel
- 이 중 CommonJS가 가장 널리 사용되었다
1. 역사
- 2005년 AJAX가 부상하며
Javascript의 중요성이 그전보다 부각 - AJAX 활성화와 함께
Javascript연산이 증가했고, 더 빠른Javascript엔진이 필요 - 2008년 Google이 공개한 V8 JS 엔진은 기존 JS엔진보다 월등히 빨랐고, 브라우저 밖에서도 충분히 쓸 만한 성능 자랑
- V8엔진의 등장으로 서버사이드 Javascript 진영에도 활기를 불어넣음
- 2009년 JS에 대한 공동 표준을 정하고 지켜나가기 위해 CommonJS그룹은 CommonJS API 0.1 발표
2. CommonJS Module 문법
require(): 모듈 가져오기module.exports: 모듈 내보내기
3. CommonJS Module의 시대는 끝났는가?
- 그렇다. 최근 많은 라이브러리들이 대부분 순수한 ESM으로 구현하고 있는 추세
4. CommonJS가 해결하려고 한 것
- 모듈화
- 스코프 : 모든 모듈은 자신만의 독립적인 실행 영역에 있어야 한다.
- 정의 : exports 객체
- Node.js에서의 module : default로 exports라는 변수를 가지고 있는 객체
- CommonJS에서는 module을 사용하지 않고 곧장 export를 사용
- 사용 : require 함수
- 함수이기에, if문 안에서도 사용이 가능하다.
5. CommonJS의 문제점
- ECMA Standard의 지원없이 독립적으로 개발
- 언어 자체로는 CJS를 포함하지 않는다. 브라우저 차원에서도 물론 지원하지 않는다.
- 정적으로 모듈을 분석하고 트리쉐이킹할 방법이 없다. 이는 번들 크기의 문제로 이어진다.
- 구현의 세부사항이 로컬 환경에 기반한 CJS는 브라우저에서 문제점이 생긴다.
- 동기적으로 로드한다. → 브라우저 환경에서 렌더링되기까지 작업이 필요하고, interaction의 block으로 이어진다.
2. ECMAScript Module
1. ECMAScript
: ECMA International이 ECMA-262기술규격에 따라 정의하고 있는 표준화된 script 프로그래밍 언어
- ECMA International : 정보 통신에 대한 표준을 제정하는 비영리 표준화 기구
- Javascript를 표준화하기 위해 만들어짐
- Action Script(Adobe Flash 사용), J스크립트(MS사의 internet explorer에 쓰임) 등 다른 구현체 역시 포함
- 1997년 1판을 시작으로 불규칙적으로 개정 → 2015년부터 매년 6월에 개정
- ES6th, 2015년(동의어 : ES2015, ECMAScript 2015)에 이전에 문제가 되었던 부분들이 해결되며 많은 기능들이 추가 → 주목과 관심
- 최신 문법은 ES13th, 2022년
- script 언어 : 독립된 시스템에서 작동하도록 특별히 설계된 프로그래밍 언어
- Javascript : ECMAScript사양을 준수하는 범용 script 언어
- Javascript코드를 이해하고 실행하는 프로그램인 Javascript Engine이 얼마나 지원하느냐에 따라 사용할 수 있는 ECMAScript 기능과 버전이 다름
2. ECMAScript Module(ESM)
: ES6에 도입된 모듈 시스템
- import, export를 사용해 분리된 Javascript 파일끼리 서로 접근할 수 있다
- 기존의 문제점
- 각각의 script 파일을 전역 스코프처럼 사용 → HTML 파일에서 보다 위에 있는 script 파일은 전역 스코프처럼 하위의 script 태그에서 접근, 변경이 가능
- 이 문제로 인해 파일 순서가 뒤틀리면 에러가 발생하고, 유지보수가 힘들어짐
- 이에 대한 해결책
- Module은 함수와 변수를 Module스코프에 넣고, 각 함수는 함수 스코프를 가짐
- export로 해당 변수, 함수를 "다른 모듈에서 import를 통해 의존할 수 있도록" 지정할 수 있음
- 브라우저에서는 ESM 방식을 사용한다.
3. ECMAScript Module 문법
import: 모듈 가져오기export: 모듈 내보내기.js확장자가 아닌.mjs확장자를 사용하면 Node.js패키지에서 ES모듈 활성화 가능(import, export 사용 가능)- 혹은
package.json파일 안에"type": "module"을 추가하면 ES모듈 활성화 가능
4. ESM과 Javascript
- Javascript는 ESM 시스템을 공식 모듈 시스템으로 사용하고 있음
- 브라우저 JS환경에서,
Javascript모듈은import,export에따라 ECMA Script Modules(ES Modules)를 가져오기, 내보내기
5. 비동기 실행
- defer : ESM에서 defer키워드가 default로 적용된다.
6. ESM은 static하다.
- import,export를 컴파일 시점에 결정한다.
- 때문에 파일 확장자와 경로를 꼭 명시해줘야 한다.
- 최상단에서 사용해야 한다.
- 하지만, dynamic import의 경우엔 사용 가능하다.
3. ESM vs CommonJS
- CJS는 Node.js의 기본값이고, ESM은 JS의 표준이다.
- ES Module형태는 JS module system이 표준화되면서 Node.js v8.5.0에 도입
- CJS는 Node.js에 내장되어 있다. Node.js에서 ESM이 도입되기 전 CJS가 Node.js모듈의 표준이었다. 고로 많은 양의 Node.js 라이브러리와 모듈은 CommonJS로 작성이 되어있다.
- 대부분의 브라우저는 ESM문법을 지원하고 React와 Vue.js같은 프레임워크에서
import/export가능하다.
- 대부분의 브라우저는 ESM문법을 지원하고 React와 Vue.js같은 프레임워크에서
- 이 프레임워크들은
Babel과 같은 트랜스파일러를 사용해서import/export를 이전 Node.js버전이 기본적으로 지원하는 CommonJS의 require()로 바꿔서 컴파일하는 것이다.
- **CommonJS의 require()**는 동기적으로 모듈을 불러온다.
- 따라서 promise나 콜백 함수를 리턴하지 않는다. 이는 대규모 애플리케이션의 경우 성능 문제가 발생할 수 있다.
- ES Module의 import는 비동기적으로 모듈을 불러온다.
- 먼저 가져온 스크립트를 바로 실행하지 않고, import나 export구문을 찾아서 스크립트를 파싱한다.
- ESM 모듈 로더는 가져온 스크립트를 비동기로 다운로드하여 파싱한 다음, import된 스크립트를 가져오고, 더 이상 import할 것이 없어질 때까지 import를 찾은 다음 dependencies의 모듈 그래프를 만든다.
- ESM 모듈 내의 자식 스크립트는 병렬로 다운로드되지만, 실행은 순차적으로 진행된다.
-
ESM에서는 require을 사용할 수 없고, import만 가능하다.
- CJS에서도 import할 수 없다.
-
ESM에서 CJS의 모듈을 import할 수 있다. 그러나 default import만 가능하다.
import _ from 'lodash'그러나, CJS가 named export하고 있다면 named import는 불가능하다.
import {shuffle} from 'lodash' -
CJS에서 ESM을 require로 가져올 수 있지만, 권장하지 않는다. 많은 보일러 플레이트가 필요하고, Webpack이나 Rollup같은 번들러가 필요하다.
-
CJS는 기본값이다. ESM모드를 사용하기 위해서는 opt-in해야 한다.
- .js → .mjs 사용
- package.json에
“type”:”module”추가 & 기존 CJS : .js → .cjs
- ESM은 CJS를 import할 수 있지만, CJS는 ESM을 import할 수 없다. CJS는 Top-level Await를 지원하지 않기 때문이다.
2. 실습
1. CJS
Node.js 환경에서 실행하였습니다.
-
a.js
const a = 5; module.exports = a; -
b.js
const a = require("./a.js"); console.log(a);
❓ 만약, import/export를 사용한다면?
![]()
ESM을 로드하려면 package.json에 “type”:”module”을 추가하거나, .mjs 확장자를 사용하세요.
결론
그렇다. Node.js는 기본적으로 CJS를 지원하기에 module.export, require문을 사용해야 한다. 이는, Node.js가 ES6이후 ESM을 지원하기도 하지만, 예전부터 CJS를 지원했기에 호환을 위해 CJS를 여전히 기본으로 제공하는 것이다.
2. ESM
-
a.mjs
const a = 1; export default a; -
b.mjs
import a from "./a.mjs"; console.log(a);
혹은, package.json에 “type”:”module”을 추가해도 된다.
-
package.json
{ "type": "module" } -
a.js
const a = 1; export default a; -
b.js
import a from "./a.js"; console.log(a);
단, “type”:”module”을 추가하면 해당 프로젝트 전체가 ESM으로 바뀌기에, CJS를 더이상 사용하지 못한다.
3. CJS와 ESM의 혼합
ESM은 CJS를 import할 수 있다.
-
c.js : CJS
const c = 1; module.exports = c; -
b.mjs : ESM
import c from "./c.js"; import a from "./a.mjs"; console.log(a, c);