kakasoo

ESM과 CommonJS의 차이 (이어서) 본문

프로그래밍/JavaScript

ESM과 CommonJS의 차이 (이어서)

카카수(kakasoo) 2022. 7. 17. 13:11
반응형

2.6.6. 비동기 임포트

ESM의 단점은, 모듈 식별자를 실행 중에 생성할 수 없다는 점, 모든 파일의 최상위에 선언되어 제어 구문 내에 포함될 수 없다는 점이다. 이는 사용자에 따라 다른 모듈을 불러야 하는 경우에 지나친 제약이 될 수 있다. 따라서 이를 극복하기 위해 비동기 임포트 ( 동적 임포트 ) 를 제공한다.

if(true) {
	require(a);
} else {
	require(b);
}
// strings-ko.js
export const HELLO = '안녕하세요.'

// strings-jp.js
export const HELLO = '오하이요';

// strings-en.js
export const HELLO = '하이'
const SUPPORTED_LANGUAGES = ['ko', 'en', 'jp'];
const selectedLanguage = process.argv[2];

if (!SUPPORTED_LANGUAGES.includes(selectedLanguage)) {
	console.error('The specified language is not supported');
	process.exit(1);
}

const translationModule = `./strings-${seletecLanguages}.js`;
import(translationModule).then((string) => {
	console.log(string.HELLO);
})

import() 연산자는 문법적으로 모듈 식별자를 인자로 취하고 모듈 객체를 Promise로 반환하는 함수와 동일하다.

node main.js ko // 실행 시 인자를 건네주면 된다.

2.6.8. 모듈의 수정

import fs from 'fs';

const originalReadFile = fs.readFile;
let mockResponse = null;

function mockedReadFile (path, cb) {
	setImmediate(() => {
		cb(null, mockedResponse)
	})
}

export function mockEnable(responseWith) {
	mockedResponse = responseWith;
	fs.readFile = mockedReadFile;
}

export function mockDisable() {
	fs.readFile = originalReadFile;
}
import fs from 'fs';
import { mockEnable, mockDisable } from './mock-read-file.js';

mockEnable(Buffer.from('Hello World'));

fs.readFile('fake-path', (err, data) => {
	if (err) {
		console.error(err);
		process.exit(1);
	}
	console.log(data.toString());
})

mockDisable();
  • 몽키 패치 방식
  • readonly live binding 방식이라고 해도 객체의 특성을 이용해서 속성을 재할당할 수 있다.
  • fs.readFile을 이 속성을 이용하여 재할당해 mock data만을 읽도록 수정하는 코드이다.
  • 만약 readFile을 읽기 전용 라이브 바인딩으로 가져온다면 위 방식은 동작하지 않는다.

ESM 환경에서 몽키 패치는 복잡하고 신뢰하기 어렵기 때문에 jest 같은 프레임워크를 사용한다.

import fs, { readFileSync } from 'fs';
import { syncBuiltinESMExports } from 'module';

fs.readFileSync = () => Buffer.from('Hello, ESM');
syncBuiltinESMExports();

console.log(fs.readFileSync === readFileSync);

module의 syncBuiltinESMExports를 이용하여 default exports 객체에 있는 속성들의 값이,

named exports와 동일한 것으로 매핑되게 할 수 있다.

2-7.ESM과 CommonJS의 차이점과 상호 운용

  1. strict mode에서 실행된다.
  2. 참조 유실 ( strict mode로 인한 )
    • require, export, module.exports, filename, dirname 등 몇 가지 참조가 정의되지 않는다.
    • 대신에 import.meta에서 데이터를 꺼낼 수 있다.
      • import.meta.url은 현재 모듈을 참조한다.
      • 문맥 상 require()도 허용할 수 있다.
      • import { createRequire } from 'module'; const require = createRequire(import.meta.url);
  3. ES에서의 this는 undefined, CommonJS에서의 this는 exports이다.
  4. ESM은 default exports에 한하여 CommonJS를 import 할 수 있다.
  5. JSON을 CommonJS처럼 가져올 수는 없다.
    • module.createRequire을 사용해서 해결할 수 있다.

읽기 전용 라이브 바인딩

ES 모듈의 또 다른 기본적인 특성은 임포트된 모듈이 익스포트된 값에 대해 읽기 전용 라이브 바인딩된다는 개념입니다.

// count.js
export let count = 0;
export function increment() {
	count++;
}
// main.js
import { count, increment } from './counter.js';
console.log(count); // 0
increment();
console.log(count); // 1
count++; // TypeError : Assignment to constant variavble!
  • 언제든지 count의 값을 읽을 수 있고, increment() 함수로 변경도 가능하지만, count 변수를 직접적으로 변경시키려 할 때에는 const 로 바인딩한 값을 변경하려 할 때와 같은 에러가 발생한다.
  • 이러한 특성을 읽기 전용 라이브 바인딩 ( readonly binding ) 이라고 한다.
  • CommonJS는 구조분해할당, 또는 얕은 복사와 같이 동작하기 때문에 값을 바꿀 수도 있고, 문제는 모듈에서는 이러한 변화를 알지 못할 것이라는 점이다.
반응형