아마도 현대 JavaScript 개발에서 ES 모듈을 사용해 보셨을 것입니다. 하지만 ES 모듈의 진화 역사를 알고 계시나요? 초기 JavaScript 실행부터 오늘날의 모듈 시스템까지의 여정을 이해하면 우리가 얼마나 멀리 왔는지, ES 모듈이 판도를 바꾸는 이유를 이해하는 데 도움이 될 것입니다.
첫 번째 웹페이지가 만들어진 지 4년 후인 1995년입니다. 대부분의 웹사이트는 단순했습니다. 즉, 텍스트가 있고 상호 작용이 최소화된 정적 페이지였습니다. 그러나 개발자들은 웹페이지를 더욱 동적으로 만드는 방법을 빠르게 찾고 있었습니다.
이러한 환경에서 Netscape(당시 주요 웹 브라우저)는 Brendan Eich를 고용하여 브라우저에서 직접 실행되는 스크립팅 언어를 만들었습니다. 이는 특히 웹 디자이너와 같이 프로그래머가 아닌 사람들을 위해 간단하고 접근 가능하도록 설계된 언어인 JavaScript의 탄생으로 이어졌습니다. 그래서 그는 10일 만에 첫 번째 버전을 완성했습니다.
원래 JavaScript는 데이터를 서버로 왕복할 필요 없이 웹페이지에 양식 유효성 검사와 같은 작은 개선 사항을 추가하기 위한 것이었습니다. 그러나 웹사이트가 더욱 상호작용적으로 변하면서 JavaScript는 원래 목적을 넘어서 빠르게 성장했습니다.
초기에는 모든 JavaScript 코드가 전역 범위에 있었습니다. 더 많은 개발자가 동일한 페이지에 코드를 추가할수록 이름 충돌 위험이 커졌습니다. 여러 스크립트에서 동일한 변수나 함수 이름을 사용한 경우 코드가 예상치 못한 방식으로 중단될 수 있습니다.
이를 관리하기 위해 개발자는 충돌을 방지하기 위해 명명 규칙을 사용했습니다. 코드 조각이 한 번만 실행되도록 의도된 경우 개발자는 이를 IIFE(즉시 호출 함수 표현식)로 래핑하는 경우가 많습니다. 이렇게 하면 함수와 변수가 함수 내에서 범위를 유지하여 전역 네임스페이스를 오염시키는 것을 방지할 수 있습니다.
(function init() { function getData(){ // ... } })()
당시에는 대부분의 웹사이트가 클라이언트측 로직이 거의 없이 서버측 렌더링되었기 때문에 이것만으로도 충분했습니다.
2008년 Ryan Dahl은 서버 측 애플리케이션 구축을 위한 JavaScript 런타임인 Node.js를 만들었습니다. 이로 인해 완전히 새로운 가능성의 세계가 열렸지만 모듈 시스템이 부족하여 개발자는 대규모 코드베이스를 관리하는 데 어려움을 겪었습니다.
2009년에 서버 측의 문제를 해결하기 위해 CommonJS가 도입되었습니다. CommonJS 모듈 시스템을 통해 개발자는 모듈을 정의하고, 기능을 공개하고, 다른 모듈을 가져올 수 있었습니다. 작동 방식의 예는 다음과 같습니다.
const math = require("./math"); function subtract(a,b) { return math.add(a,-b); } module.exports = { subtract: subtract }
CommonJS를 사용하면 각 파일이 자체 모듈로 처리되며, require 함수를 사용하여 모듈을 가져오고 module.exports를 사용하여 내보냅니다.
CommonJS의 주요 기능은 다음과 같습니다.
모듈이 필요할 때 파일 확장자는 선택 사항입니다(예: require('./math')는 자동으로 math.js를 찾습니다).
모듈 로드는 동기식입니다. 즉, 프로그램은 실행을 계속하기 전에 모듈이 로드될 때까지 기다립니다.
기사 뒷부분에서 Ryan Dahl이 두 가지 디자인 결정을 후회한다고 인정한 이유를 살펴보겠습니다.
동시에 AMD(Asynchronous Module Definition)라는 또 다른 모듈 시스템이 개발되었습니다. CommonJS는 주로 서버측 JavaScript에 중점을 두었지만 AMD는 브라우저에서 클라이언트측 JavaScript를 처리하도록 설계되었습니다.
AMD의 주요 기능은 모듈을 비동기적으로 로드하는 기능이었습니다. 이를 통해 브라우저는 언제든지 페이지에 필요한 JavaScript만 로드할 수 있어 초기 페이지 로드 시간이 단축되어 성능이 향상되었습니다. 또한 종속성 해결과 관련된 문제를 해결하여 종속성 로드가 완료된 후에만 모듈이 실행되도록 했습니다.
AMD의 혜택은 다음과 같습니다.
2010년 npm(서버 측 JavaScript용 패키지 관리자)이 등장하면서 브라우저와 서버 전반에서 코드를 공유해야 할 필요성이 분명해졌습니다. 브라우저 환경에 맞게 코드를 변환하여 개발자가 브라우저에서 CommonJS 모듈을 사용할 수 있도록 해주는 도구인 Browserify를 입력하세요.
CommonJS와 AMD라는 두 가지 경쟁 표준이 있습니다. 빌드 단계 없이 어디서나 작동할 수 있는 단일 모듈 시스템이 필요했습니다. 그리고 2011년에는 UMD(Universal Module Definition)가 도입되었습니다.
UMD는 두 가지 장점을 결합하여 개발자가 다음에서 실행할 수 있는 모듈을 작성할 수 있도록 합니다.
UMD는 Lodash, Underscore.js, Backbone.js 및 Moment.js와 같은 유명한 라이브러리를 채택하여 라이브러리 작성자들 사이에서 매우 인기가 있었습니다. 그러나 UMD에는 몇 가지 중요한 단점이 있었습니다. 호환성 문제를 해결했지만 두 시스템을 모두 관리해야 하는 복잡성이 발생했으며 AMD와 CommonJS의 문제를 모두 물려받았습니다.
2015년 ES 모듈(ESM)이 ECMAScript 표준의 일부로 도입되어 마침내 JavaScript용 기본 모듈 시스템을 제공했습니다. 2017년에는 모든 주요 브라우저가 ES 모듈을 지원했고, 2020년에는 Node.js에도 지원이 추가되었습니다.
ES 모듈이 왜 최고인지 살펴보겠습니다.
다음 UMD 코드:
(function init() { function getData(){ // ... } })()
이제 다음으로 줄일 수 있습니다.
(function init() { function getData(){ // ... } })()
공평하게 말하면 실제로 UMD를 그렇게 쓴 사람은 없습니다. 그들은 해당 코드를 생성하기 위해 umdify와 같은 도구를 사용했습니다. 하지만 ES 모듈이 내장되어 있으면 빌드 단계를 건너뛰고 번들 크기를 더 줄일 수 있습니다.
ES 모듈은 정적입니다. 즉, 도구는 컴파일 타임에 코드 구조를 분석하여 어떤 코드가 사용되고 어떤 코드가 사용되지 않는지 결정할 수 있습니다. 이를 통해 사용되지 않은 코드가 최종 번들에서 제거되는 트리 쉐이킹이 가능해집니다.
CommonJS 및 AMD 모듈은 동적이기 때문에(런타임에 평가됨) 이러한 시스템에서는 트리 쉐이킹의 효율성이 훨씬 떨어지며 번들이 더 커지는 경우가 많습니다.
CommonJS로 모듈을 가져올 때 파일 확장자를 지정하는 것은 선택 사항입니다.
const math = require("./math"); function subtract(a,b) { return math.add(a,-b); } module.exports = { subtract: subtract }
그런데 수학이란 실제로 무엇을 의미하는 걸까요? 자바스크립트 파일인가요? JSON 파일? 수학 디렉토리 안에 index.js 파일이 있나요?
ES Lint와 같은 정적 분석 도구를 사용하면 TypeScript 또는 Prettier의 모든 요구 사항이 추측 게임이 됩니다.
math.js인가요?
math.jsx인가요?
math.cjs인가요?
math.mjs인가요?
math.ts인가요?
math.tsx인가요?
math.mts인가요?
math.cts인가요?
math/index.js인가요?
math/index.jsx인가요?
아이디어가 이해되실 겁니다.
파일을 읽는 데 비용이 많이 듭니다. 메모리에서 읽는 것보다 성능이 훨씬 떨어집니다. math/index.js를 가져오면 IO 작업이 1번이 아닌 9번 발생합니다! 그리고 이 추측 게임은 도구 속도를 저하시키고 개발자 경험을 저하시킵니다.
ES 모듈에서는 파일 확장자를 필수로 지정하여 이러한 혼란을 방지합니다.
모듈을 동기식으로 로드하는 CommonJS와 달리(모듈이 로드될 때까지 전체 프로세스를 차단) ES 모듈은 비동기식입니다. 이를 통해 모듈이 백그라운드에서 로드되는 동안 JavaScript가 계속 실행되어 성능이 향상됩니다. 특히 Node.js와 같은 환경에서 더욱 그렇습니다.
명확한 이점에도 불구하고 ES 모듈을 채택하는 것은 간단한 작업이 아니었습니다. 전환이 너무 오래 걸린 이유는 다음과 같습니다.
CommonJS에서 ES 모듈로 전환하는 것은 특히 대규모 프로젝트의 경우 사소한 변화가 아니었습니다. 도구 지원의 필요성과 구문 차이로 인해 마이그레이션이 상당한 노력을 기울였습니다.
node.js가 ES 모듈을 완전히 지원하는 데 5년이 걸렸습니다. 이 기간 동안 개발자는 CommonJS(서버) 및 ES 모듈(브라우저)과의 호환성을 유지해야 했습니다. 이러한 이중 지원은 생태계에 많은 마찰을 일으켰습니다.
Node.js가 ES 모듈에 대한 지원을 추가한 후에도 CommonJS 모듈은 ES 모듈을 로드할 수 없었습니다. ES 모듈은 CommonJS 모듈을 로드할 수 있었지만 두 시스템은 완전히 상호 운용되지 않았으므로 두 시스템을 모두 지원해야 하는 패키지 작성자에게 추가적인 골칫거리가 되었습니다.
JavaScript 모듈의 미래는 밝습니다. ES 모듈을 지배적인 시스템으로 만들 수 있는 몇 가지 주요 개발 사항은 다음과 같습니다.
Node.js 23에서는 마침내 CommonJS에서 ES 모듈을 로드하는 기능이 생겼습니다.
작은 주의 사항이 있습니다. 최상위 수준 Wait를 사용하는 ES 모듈은 CommonJS로 가져올 수 없습니다. 왜냐하면 Wait는 비동기 함수에서만 사용할 수 있고 CommonJS는 동기식이기 때문입니다.
npm과 경쟁하는 새로운 자바스크립트 패키지 레지스트리입니다. 여기서는 다루지 않겠지만 npm에 비해 많은 장점이 있습니다. 하지만 흥미로운 점은 ES 모듈 패키지만 업로드할 수 있다는 것입니다. 기존 표준을 지원할 필요가 없습니다.
글로벌 범위의 해킹에서 최신 ES 모듈로의 여정은 JavaScript 구성 방식을 변화시켰습니다. CommonJS, AMD 및 UMD를 수년간 실험한 끝에 ES 모듈은 더 간단한 구문, 더 나은 최적화 및 향상된 성능을 제공하는 명확한 표준으로 부상했습니다.
ES 모듈로 마이그레이션하는 것은 어려운 일이었지만 특히 Node.js 지원 및 생태계 호환성과 관련하여 이점은 부인할 수 없습니다. 상호 운용성을 향상시키는 Node.js 23과 통합 모듈 시스템을 촉진하는 JSR과 같은 새로운 도구를 통해 ES 모듈은 JavaScript의 기본값이 되도록 설정되었습니다.
ES 모듈을 계속 수용하면서 JavaScript 개발에서 모듈화의 새로운 시대를 여는 더 깔끔하고, 더 빠르고, 유지 관리하기 쉬운 코드를 기대할 수 있습니다.
위 내용은 ES 모듈의 간략한 역사의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!