본문 바로가기
반응형

 이번주에는 WebPack에 대해 카터에게 물어봤다.(카터는 신이야) 그런데 웹팩이 궁금했는데, 갑자기 JS의 역사와 함께 내가 모르는 개념(개념까지도 아니다. 그냥 사실이다. 난 사실을 몰랐던 것이다)들을 알게되었고, 그 중에서 웹팩에 대해 알기 위해 CommonJS와 ESModule에 대해 먼저 공부해 보았다. 

 

CommonJS와 ESModule (ECMAScript Module)이란?

CommonJS와  ESModule (ECMAScript Module)은 자바스크립트에서 모듈을 관리하고 조직화하는 두 가지 주요 방식이다. 모듈을 관리하고 조직화한다는 것은, 코드를 독립적인 단위로 나누어 재사용 가능하고 의존성을 쉽게 관리할 수 있도록 만드는 것을 의미한다. 

아직은 잘 이해가 가지 않으니 더 깊게 이야기 해보자. 

 

CommonJS와 ESModule (ECMAScript Module)의 역사

모듈 시스템의 역사는 JavaScript의 발전 과정과 밀접하게 연관되어 있다. 자바스크립트가 처음 개발될 때 모듈화를 염두에 두지 않았기 때문에, 복잡한 애플리케이션을 구성할 때 코드의 유지보수성과 재사용성이 떨어지는 문제가 있었고, 이에 따라 다양한 모듈화 방식이 제안되었다고 한다. 

CommonJS의 탄생 배경

  CommonJS는 2009년에 자바스크립트를 서버 환경에서 사용할 수 있도록 하기 위해 만들어진 모듈 시스템이다.

 Node.js가 발표된 이후, 자바스크립트가 서버 환경에서도 사용되기 시작하면서 복잡한 서버 애플리케이션을 관리할 수 있는 모듈화 방식이 필요하게 되었다. 기존 자바스크립트에는 파일 간의 의존성을 관리하거나 모듈화할 수 있는 표준적인 방법이 없었기 때문에, Node.js 커뮤니티에서 독립적인 모듈 시스템을 제안하게 되었고, 그 결과 require와 module.exports를 기반으로 한 CommonJS가 등장하게 되었다.

 CommonJS는 Node.js의 기본 모듈 시스템으로 자리 잡았으며, 서버 환경에서 각 모듈을 동기적으로 로딩하여 파일 간의 의존성을 쉽게 관리할 수 있는 특징을 가지고 있다. 그러나 Node.js가 ESModule을 지원하기 시작하면서 CommonJS의 중요성은 점차 줄어들고 있다. 하지만, 여전히 많은 기존 프로젝트와 라이브러리가 CommonJS를 사용하고 있어, ESModule로의 완전한 전환에는 시간이 필요할 것이라고 한다.

ESModule (ECMAScript Module)의 등장 배경과 역사

 ESModule은 2015년 ECMAScript 2015(ES6)에서 공식적으로 표준화된 모듈 시스템이다. 브라우저 환경에서는 2017년부터 주요 브라우저(Chrome, Firefox, Safari 등)에서 import와 export 키워드를 기본 지원하기 시작했으며, 이를 통해 클라이언트 사이드에서도 모듈화를 일관되게 사용할 수 있게 되었다.

 자바스크립트는 본래 브라우저 환경에서 사용하는 스크립트 언어였기 때문에, 초기에는 모듈화를 지원하지 않았다. 이에 따라 서버와 브라우저에서 각각 다른 모듈 시스템(예: CommonJS, AMD, UMD 등)을 사용하는 문제가 발생하게 되었고, 이 문제를 해결하고자 자바스크립트를 브라우저와 서버 환경 모두에서 일관되게 사용할 수 있도록 ECMAScript 표준화 기구인 TC39가 새로운 모듈 시스템을 제안하게 되었다. ESModule은 import와 export 키워드를 사용하여 파일 간의 의존성을 관리하며, 코드의 정적 분석을 지원하여 최적화가 가능하도록 설계되었다. 또한, 브라우저와 Node.js 환경에서 모두 사용할 수 있는 유일한 표준 모듈 시스템으로 자리 잡게 되었다.

CommonJS와 ESModule의 구체적인 차이점

CommonJS와 ESModule은 각각의 특징과 동작 방식에서 큰 차이점을 가지고 있다. 이 차이점은 주로 모듈을 가져오고 내보내는 방식, 모듈 로딩 시점, 스코프 처리, 그리고 호환성 문제에서 발견할 수 있다. 

 

1. 모듈 가져오기와 내보내기 방식의 차이

  • CommonJS동기적으로 모듈을 가져오며, require()를 통해 다른 모듈을 로드한다.
     require()는 함수 호출과 같이 사용되며, 모듈이 호출되는 시점에 바로 실행된다. 즉, 모듈 로딩이 순차적으로 이루어지기 때문에 서버 환경에서는 안정적이지만, 브라우저 환경에서는 성능 이슈가 발생할 수 있다.
const myModule = require('./myModule'); // 모듈을 동기적으로 로딩
module.exports = { myFunction }; // module.exports로 모듈 내보내기
  • ESModule은 정적 분석이 가능하도록 비동기적으로 모듈을 가져온다. import와 export 키워드를 사용하여 모듈을 가져오고 내보낸다. import는 문법적으로 파일 최상단에서만 사용할 수 있고, 코드 실행 전에 모듈의 의존성을 파악하여 비동기로 로딩한다. 이를 통해 브라우저 환경에서 더 나은 성능 최적화를 제공할 수 있다.
import { myFunction } from './myModule'; // 모듈을 비동기적으로 로딩
export const anotherFunction = () => {}; // export 키워드로 모듈 내보내기

2. 모듈 로딩 시점의 차이

  • CommonJS런타임(runtime)에서 모듈을 로딩한다. 즉, 코드가 실행될 때 모듈을 require()를 통해 순차적으로 로드하고 실행한다. 이 때문에 로드 순서가 중요하며, 모듈이 서로 순환 참조를 할 경우 문제가 발생할 수 있다.
// 순차적 로딩: 실행 시에 로드됨
const moduleA = require('./moduleA');
  • ESModule 컴파일 타임(compile-time)에 모듈을 분석한다. import 문을 사용하면, 코드가 실행되기 전에 미리 모듈의 구조를 파악하고 모든 의존성을 비동기로 로드한다. 이를 통해 *트리 쉐이킹(tree-shaking)과 같은 최적화 작업을 할 수 있다.
    *트리 쉐이킹(tree-shaking)은 불필요한 코드를 제거하여 최종 번들 크기를 줄이는 코드 최적화 기법이다.
// 정적 분석: 컴파일 시점에 로드됨
import { moduleB } from './moduleB';

3. 순환 참조 처리 방식의 차이

순환 참조란 두 개 이상의 모듈이나 객체가 서로를 참조하거나 스스로를 참조하는 상황을 의미한다. 순환 참조가 발생하면 모듈 간의 의존성이 서로 물고 물리는 관계가 되어, 코드 실행 시 무한 루프, 메모리 누수, 모듈이 완전히 초기화되지 않는 문제 등 다양한 오류를 발생시킬 수 있다. 

  • CommonJS는 모듈 간의 순환 참조가 발생할 경우, 모듈을 로드하는 도중 불완전한 모듈 객체를 반환한다. 즉, 아직 완전히 정의되지 않은 상태의 모듈이 참조될 수 있다.
const moduleB = require('./moduleB'); // 불완전한 모듈을 참조할 수 있음
  • ESModule은 순환 참조가 발생할 경우, 모듈 내의 모든 import와 export가 참조 형태로 처리되기 때문에, 순환 참조 문제를 더 안전하게 관리할 수 있다. 이를 통해 모듈 간의 의존성이 정적으로 해결되며, 순환 참조로 인한 오류가 줄어든다.
import { valueB } from './moduleB';

4. 디폴트 익스포트와 명명된 익스포트의 차이

  • CommonJS에서는 기본적으로 module.exports를 사용하여 하나의 모듈 전체를 내보낸다. 즉, module.exports를 통해 내보낸 객체가 모듈 전체를 나타낸다.
module.exports = { functionA, functionB }; // 모듈 전체를 단일 객체로 내보냄
  • ESModule은 export default를 통해 export default를 지원하고, 동시에 export 키워드를 사용하여 명명된 익스포트를 지원한다. 이를 통해 모듈 내의 특정 요소들만 선택적으로 내보낼 수 있다.
export default functionA;
export const functionB = () => {};

 

 

반응형

'코딩 > Web' 카테고리의 다른 글

HTTP의 구조와 Method  (2) 2024.11.09