Commonjs, Esm 모듈 , 순환 참조 차이

yoeubi
4 min readJan 23, 2021

--

출처: https://twitter.com/Manz/status/1265341200007036929?s=20

TL;DR

  1. Commonjs는 exports 객체에 값을 복사(call by value, reference)해서 넣지만 Esm는 참조를 반환하는 함수를 정의한다.
  2. Esm은 Commonjs와 달리 import한 변수들은 readonly이다.
  3. Commonjs, Esm 모듈은 캐싱이 된다. 그러기에 다 평가되지 않은채로 캐싱되었다면 사용하는 쪽에서 문제가 발생한다.

Commonjs와 Esm 구현의 차이

왼쪽 Commonjs / 오른쪽 Esm

foo, bar 변수를 선언하고 setTimeout으로 1초뒤에 변수들을 다른 값으로 대입한다면 setTimeout에 있은 console.log에서도 동일하게 반영이 될까요?

위 코드를 webpack으로 빌드하고 실행해보면 다음과 같은 결과가 나옵니다.

같은 코드이지만 출력된 결과물은 다른데요

어떻게 이런 출력이 나왔는지 webpack에서 빌드된 코드를 살펴보겠습니다.

Commonjs 방식에서는 modules 객체를 파라미터로 넣어서 modules.exports 객체에 직접 할당하는 방식입니다. 그래서 foo,bar의 값들이 복사됩니다.

Esm에서는 exports 객체에 해당 변수를 반환하는 함수를 만듭니다.

그래서 setTimeout으로 변수값들을 바꾸면 Commonjs에서는 반영안되지만 Esm에서는 제대로 되는 이유입니다.

전체 코드는 맨 아래에 있습니다.

Commonjs, Esm 순환 참조 차이

A, B모듈이 서로를 참조하는 것을 순환 참조라고 하는데 commonjs에서는 빈객체를 반환하지만 esm에서는 ReferenceError를 출력합니다.

webpack에서는 이러한 순환참조를 어떤식으로 변환을 하고 왜 다른지를 알아보겠습니다.

commonjs

a,b 파일 서로 참조하는 코드입니다. 이걸 실행하면 다음과 같은 결과를 나타냅니다.

이러한 결과를 보여주는 이유는 모듈이 캐싱이 된다는 점,

모듈을 어떻게 정의하냐에서 차이를 보여줍니다.

webpack에서는 모듈 파일을 읽을때 캐시(__webpack_module_cache__)에서 먼저 찾습니다.

없으면 exports 객체가 담긴 객체로 초기화하고 캐시에도 넣습니다.

그리고 해당 모듈 파일을 실행합니다

esm 도 동일하게 캐싱된 객체를 가지고 오지만 모듈정의 방식이 다르기 때문에 Reference 에러가 나옵니다.

전체 코드는 맨 아래에 있습니다.

Commonjs, Esm 모듈 차이 코드

commonjs
esm

Commonjs, Esm 순환 참조 코드

commonjs
esm

출처, 참고 링크

The difference between commonjs and esm

module

--

--