‘그냥 import 쓰면 되잖아’가 나오기까지의 긴 여정
JavaScript 프로그램은 처음에는 아주 작고 단순했습니다. 초기 웹 페이지에서 사용된 대부분의 스크립트는 특정 기능 하나만 수행하는 작은 스니펫 형태였기 때문에, 파일 간 의존성 관리나 복잡한 구조가 필요하지 않았습니다.
하지만 시간이 지나면서 웹 애플리케이션 규모가 커지고, JavaScript가 다양한 역할을 수행하게 되면서 기존 방식만으로는 유지보수와 확장성에 한계가 드러났습니다.
초기 JavaScript는 브라우저 전용 스크립트 언어로 설계되었고, 모듈 개념이 존재하지 않았습니다.
그 결과 다음과 같은 문제가 자주 발생했습니다.
<script>태그를 여러 개 로드하면서 생기는 전역 스코프 오염- 스크립트 로드 순서에 따른 예기치 않은 오류
- 대규모 애플리케이션 구성의 어려움
결국 개발자들은 “점점 커지는 JavaScript 코드베이스를 어떻게 구조적으로 관리할 것인가?”라는 문제에 직면하게 되었고, 다양한 해결책을 모색하기 시작했습니다.
Node.js와 CommonJS의 등장
2008년 Node.js가 등장하면서 JavaScript의 활용 범위는 브라우저를 넘어 확장되었습니다.
브라우저 외부 환경에서는 모듈화가 필수적이었고, 이러한 요구를 충족하기 위해 CommonJS 모듈 시스템이 널리 사용되기 시작했습니다.
CommonJS의 특징은 다음과 같습니다.
- 파일 단위의 모듈
- require()를 통한 동기적 로딩
- module.exports로 모듈을 외부에 노출
// a.js
module.exports = function add(a, b) { return a + b }
// b.js
const add = require('./a')
console.log(add(1,2))
CommonJS는 Node.js 생태계에서 큰 성공을 거두었고, 서버 사이드 JavaScript 개발의 표준처럼 받아들여졌습니다.
브라우저에도 모듈 시스템이 필요하다
CommonJS가 널리 사용되면서 브라우저에서도 비슷한 모듈화 경험을 요구하는 개발자들이 많아졌습니다.
하지만 브라우저는 스크립트를 동기적으로 로드할 경우 메인 스레드가 블로킹되는 문제가 있어(UI를 담당하는게 메인 임무이므로), Node.js 방식 그대로 가져올 수는 없었습니다.
이런 배경에서 등장한 것이 AMD(Asynchronous Module Definition) 입니다.
// math.js
define([], function () {
return {
add: (a, b) => a + b,
multiply: (a, b) => a * b
};
});
AMD는 비동기 로딩을 지원하여 브라우저 환경에 최적화된 방식이었지만, 다음과 같은 단점이 있었습니다.
- 문법이 장황하고 개발자 경험이 좋지 않음
- Node.js의 CommonJS와 문법이 달라 학습비용 증가
- 결국 생태계 표준으로 자리잡지 못함
하지만 이 시기를 통해 “브라우저에도 본격적인 모듈 시스템이 반드시 필요하다”는 공감대가 생기기 시작했습니다.
이 사이트를 참고하시면 좋을것 같습니다.
Node.js와 브라우저 모두를 위한 모듈 시스템: UMD
개발자들은 Node.js와 브라우저 양쪽에서 사용 가능한 모듈 방식이 필요했습니다.
이 요구를 충족하기 위해 탄생한 것이 UMD(Universal Module Definition) 입니다.
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define([], factory)
} else if (typeof module === 'object' && module.exports) {
module.exports = factory()
} else {
root.myLib = factory()
}
}(this, function () {
return {}
}))
UMD는 말 그대로 “Universal”을 목표로 했지만, 근본적인 해결책이라기보다는 기존 시스템(CommonJS + AMD)을 감싸는 래퍼 형태였습니다.
즉, 모듈 시스템이 언어 차원에서 표준화되지 않은 이상 근본적 문제는 해결하기 어렵다는 사실을 보여주었습니다.
모듈 혼란의 종지부, ESM
2015년, ES6(ES2015)에서 마침내 ECMAScript 공식 모듈 시스템(ES Modules, ESM) 이 도입되었습니다.
ESM은 다음과 같은 특징을 가지고 있습니다.
- import / export 문법
- 정적 분석 가능 → 트리 셰이킹(tree shaking) 가능
- 브라우저와 Node.js 모두에서 공식 지원
- 언어 차원에서 처음으로 제공된 모듈 시스템
// add.js
export function add(a, b) { return a + b }
// main.js
import { add } from './add.js'
브라우저에서도 다음과 같이 선언하면 바로 사용할 수 있습니다.
<script type="module" src="/main.js"></script>
ESM의 가장 큰 장점은 다음과 같습니다.
- 문법이 간결하고 직관적임
- 코드만 봐도 어떤 모듈이 import/export 되는지 즉시 파악 가능
- JavaScript 개발자들이 공통 모듈 문법을 사용하게 됨
- 번들러 없이도 브라우저에서 직접 모듈 사용 가능
많은 개발자들이 “드디어 JavaScript가 제대로 된 모듈 시스템을 갖추었다”라고 느끼게 된 시점이기도 합니다.
마무리
JavaScript 모듈 시스템은 단순한 스크립트 언어 시절을 지나, 웹과 서버 전반을 아우르는 대규모 애플리케이션 개발을 지원하기 위해 꾸준히 발전해 왔습니다.
- 전역 스코프 기반의 초기 방식
→ CommonJS와 AMD의 등장
→ 양쪽을 아우르려 한 UMD
→ 그리고 언어 차원의 표준인 ESM 등장
이 흐름은 JavaScript가 단순한 브라우저 스크립트 언어를 넘어, 현대 소프트웨어 개발에서 중요한 위치를 차지하게 된 과정을 잘 보여줍니다.
Member discussion