duck blog
JavaScript 모듈 시스템의 역사와 발전 CommonJS에서 ES6 모듈까지

JavaScript 모듈 시스템의 역사와 발전 CommonJS에서 ES6 모듈까지

최근에 build 환경을 개선하려고 여러가지 파고들다 보니 JS의 모듈에 대한 역사를 정리할 필요가 있어 이번 기회에 정리하고자 한다. 이 글에서는 JavaScript 모듈 시스템의 역사와 발전 과정을 살펴보며, CommonJS의 등장 배경과 ES6 모듈 시스템의 도입 이유, 그리고 브라우저 환경이 어떻게 변했는지 알아보려 한다.

1. 초기 JavaScript의 모듈화 방식

1.1 전역 변수 사용

JavaScript가 처음 등장했을 때는 모듈 시스템이 없어서, 스크립트 간의 코드를 공유하기 위해 전역 변수를 사용했다. 이는 모든 스크립트가 전역 범위에서 실행되며 서로 접근할 수 있다는 의미다. 이 방식은 1995년 JavaScript가 처음 등장한 이후부터, 2015년 ECMAScript 6의 모듈 시스템이 표준화되기 전까지 계속 사용되었다.

한계

  • 전역 변수 충돌: 여러 스크립트가 동일한 전역 변수를 사용하면 충돌이 발생할 수 있다.
  • 유지보수 어려움: 전역 범위에 많은 변수가 존재하면 코드가 복잡해지고 유지보수가 어려워진다.

1.2 IIFE (Immediately Invoked Function Expressions)

전역 변수 오염을 방지하기 위해 IIFE 패턴이 도입되었다. IIFE는 함수를 즉시 실행하여 모듈을 캡슐화하는 방식이다.

사용 이유

  • 전역 범위 오염 방지: IIFE는 클로저를 활용하여 내부 변수들이 전역 범위를 오염시키지 않도록 한다. 이를 통해 전역 변수 충돌을 피할 수 있다.
  • 블록 스코프 활용: 자바스크립트가 ES6 이전에는 블록 스코프를 지원하지 않았기 때문에, IIFE를 통해 특정 코드 블록 내에서만 유효한 변수를 생성하고 관리할 수 있었다.

2. CommonJS의 등장

2.1 CommonJS의 정의와 목적

CommonJS는 서버 측 JavaScript 애플리케이션을 위한 모듈 시스템으로, 주로 Node.js 환경에서 사용된다. 모듈을 정의하고 불러오는 방식을 표준화하여 코드의 재사용성을 높이고 유지보수를 용이하게 한다.

2.2 작동 방식

  • require 함수로 모듈을 불러오고, module.exports 또는 exports 객체로 모듈을 내보낸다.
  • 예시:
  // math.js
  module.exports.add = (a, b) => a + b;

  // app.js
  const math = require('./math');
  console.log(math.add(2, 3)); // 5

2.3 역사

CommonJS는 2009년에 시작되었다. Node.js가 CommonJS 모듈 시스템을 채택하면서 널리 사용되기 시작했다. 당시에는 ES6(ECMAScript 2015) 이전이었고, JavaScript에 모듈 시스템이 내장되어 있지 않았기 때문에 CommonJS가 표준으로 자리잡았다.

3. 브라우저 환경의 변화와 번들러의 등장

3.1 브라우저에서의 CommonJS 사용

브라우저는 기본적으로 CommonJS 모듈 시스템을 이해하지 못한다. 이를 해결하기 위해 번들러가 개발되었다. 번들러는 여러 모듈을 하나의 파일로 묶어 브라우저가 이해할 수 있는 형식으로 변환해준다.

3.2 주요 번들러

3.2.1 Browserify

Browserify는 CommonJS 스타일 모듈을 브라우저에서 사용할 수 있게 번들링하는 도구다.

browserify app.js -o bundle.js

3.2.2 Webpack

Webpack은 모듈 번들러로, 코드 스플리팅과 트리 셰이킹 같은 고급 기능을 제공한다.

// webpack.config.js
module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: __dirname + '/dist'
  }
};

3.2.3 Rollup

Rollup은 ES6 모듈을 지원하는 번들러로, 주로 라이브러리 빌드에 사용된다.

import resolve from 'rollup-plugin-node-resolve';
export default {
  input: 'src/index.js',
  output: {
    file: 'bundle.js',
    format: 'cjs'
  },
  plugins: [resolve()]
};

4. ES6 모듈 시스템의 도입

4.1 ES6 모듈의 정의와 목적

ES6 모듈 시스템은 JavaScript 언어 자체에 내장된 모듈 시스템으로, 모듈을 정의하고 불러오는 표준 방법을 제공한다. importexport 구문을 사용하여 모듈을 가져오고 내보낸다.

4.2 작동 방식

  • importexport 구문을 사용한다.
  • 예시:
    // math.js
    export function add(a, b) {
      return a + b;
    }
    
    // app.js
    import { add } from './math.js';
    console.log(add(2, 3)); // 5
    

4.3 브라우저에서의 사용

브라우저에서 ES6 모듈을 사용하려면 script 태그의 type="module" 속성을 지정한다.

<script type="module">
  import { add } from './math.js';
  console.log(add(2, 3)); // 5
</script>

5. 최신 빌드 도구의 등장

5.1 ESBuild

ESBuild는 Go 언어로 작성된 초고속 빌드 도구로, JavaScript와 TypeScript 파일을 매우 빠르게 번들링하고 트랜스파일링한다.

esbuild src/app.js --bundle --outfile=dist/out.js

5.2 Vite

Vite는 ESBuild를 기반으로 하는 차세대 프론트엔드 빌드 도구로, 개발 서버와 번들링 기능을 제공한다.

// vite.config.js
import { defineConfig } from 'vite';
export default defineConfig({
  // Vite configuration
});

결론

JavaScript 모듈 시스템은 초기의 전역 변수 사용과 IIFE 패턴에서 시작하여, CommonJS와 AMD의 등장, 그리고 ES6 모듈 시스템의 표준화와 최신 빌드 도구의 발전에 이르기까지 많은 변화를 겪어왔다. 이러한 변화는 JavaScript 개발 환경을 더욱 구조화되고 효율적으로 만들었으며, 개발자들이 더 나은 코드 재사용성과 유지보수성을 실현할 수 있도록 도와주었다.


이번 글에서는 JavaScript 모듈 시스템의 역사와 발전 과정을 살펴보았다. CommonJS의 등장 배경과 ES6 모듈 시스템의 도입 이유, 그리고 이로 인해 브라우저 환경이 어떻게 변했는지를 이해함으로써, 현재의 빌드 환경을 더 잘 이해할 수 있을 것이다. 앞으로도 계속해서 발전하는 JavaScript 생태계의 변화를 주목해보자. (다음은 엄청난 성능의 Rust 빌드 도구가 나올지...)