JavaScript 모듈 시스템 알아보기 - 모듈의 역사
JavaScript
는 초기 개발 기간이 짧았던 만큼 완성도가 낮고 문제점이 많은 언어였습니다. 시간이 지나면서 많이 성숙해지긴 했으나, 브라우저의 특수성 때문에 하위호환성을 갖춰야 할 필요가 있고, 이에 과거 문제가 많았던 기능은 그대로 지원되며 개선된 기능은 새로운 문법으로 추가되는 방식으로 발전해 왔죠. 그렇기에 문제가 많아 현재는 권장되지 않는 과거 문법 또한 이해할 필요는 있습니다. 그중 모듈에 대해서 알아보겠습니다.
코딩은 한 파일에서만 이뤄지지 않습니다. 협업, 가독성, 재사용성 등을 고려하여 여러 소스 파일로 분산해서 작업합니다. 모듈 시스템은 용도에 따라 파일을 분리하여 독립적으로 기능을 구현하고, 이 파일들이 서로 참조될 수 있도록 하며, 대부분의 프로그래밍 언어에 존재하는 개념입니다. JavaScript
초기에는 모듈 시스템이 존재하지 않았습니다. 모듈의 부재로 인한 문제점을 체감하던 여러 개발 커뮤니티는 이를 해소하기 위한 다양한 시도가 있었으며, 그 결과 현재 모듈 단위로 개발하는 것이 주류로 자리 잡고 있습니다.
모듈 도입 전
JavaScript
파일은 <script>
태그를 통해 로드되며, 여러 파일이 존재하는 경우 파일 전부 다 추가해 줘야 합니다.
<script src="file1.js"></script>
<script src="file2.js"></script>
JavaScript
에 모듈 개념이 존재하지 않던 때에는 파일이 여럿 존재하더라도 전부 같은 global scope를 공유했습니다. 그렇기에 각 파일의 최상단에 선언한 변수는 모든 파일에서 공유되며, 이는 많은 문제점을 보여줍니다. 한 파일에서 사용하는 변수가 다른 파일에서도 사용되는 상황이라면 이를 추적하는 것이 직관적이지 않고, 변수명에 신경 쓰지 않는다면 의도치 않게 override를 해버리는 상황도 생길 수 있습니다. 여러 문제점 때문에 scope를 내부적으로 분리하기 위해 Immediately Invoked Function Expressions (IIFE) 같은 방법을 사용하기도 했습니다.
// IIEF
(function () {
// Module code here
})();
// IIEF with return
var module = (function () {
// Module code here
})();
2009: CommonJS (CJS)
JavaScript
를 브라우저 밖에서 다용도로 사용하고자 하는 여러 시도가 있었고 대표적으로 Node.js
가 있습니다. CommonJS
는 이 생태계에서 Module 시스템에 대한 표준을 설립하기 위한 프로젝트입니다. 브라우저 기반 JavaScript
와 다르게 모든 파일이 하나의 global scope를 공유하는 것이 아닌 각 파일이 독립적인 scope를 갖습니다. 또한 exports
와 require
문으로 의존성을 관리합니다.
// import
const mod = require("./modulePath");
// export
const value = "A value to be exported";
exports.value = value;
2009: Asynchronous Module Definition (AMD)
지금은 거의 보이지 않는 규격으로 CommonJS
와 비슷한 시기에 탄생했으며, CommonJS
와는 다르게 브라우저 기반 JavaScript
에서 모듈 시스템을 구현하기 위한 규격입니다. 대표적으로 RequireJS
가 있으며, define
함수를 통해 모듈을 정의합니다. RequireJS
는 외부 라이브러리이기 때문에 해당 라이브러리 스크립트 파일을 따로 추가해 줘야 합니다.
<script src="pathToRequireJS" data-main="entry.js"></script>
define(["module1", "module2"], function (mod1, mod2) {
// Module code goes here
});
2011: Module Bundler
Module Bundler는 사용자의 코드와 종속성을 하나의 JavaScript
파일로 합쳐주는 도구로 대표적으로 Webpack
, Rollup
등이 있습니다.
Module Bundler는 Node.js
환경을 기반으로 하며, 빌더와 같은 역할을 합니다. Module Bundler를 통해 번들링을 하게 되면 Node.js
의 내장 모듈 시스템으로 엮여있는 여러 파일들을 하나의 파일로 합쳐주게 되며, 그 결과물은 단일 파일이기에 사실상 별도의 모듈 시스템을 필요로 하지 않습니다. 따라서 모듈이 지원되지 않는 환경과 호환되어 마치 모듈을 사용하고 있는 듯한 느낌을 줄 수 있습니다.
Module Bundler는 모듈 시스템에 대해서 새로운 규격을 정의하지는 않지만, 자동화, 최적화, 확장성의 측면에서 여러 가지 기여를 합니다. 특히 코드를 파일 시스템에서 로드하는 Node.js
에 비해 HTTP 요청으로 로드하는 브라우저 환경에서 더더욱 그 최적화를 체감할 수 있습니다. 그렇기에 현재 많은 프론트엔드 프레임워크 및 개발 환경 구축에 있어서 필수로 사용되는 툴로 자리 잡고 있습니다.
2015: ES Module (ESM)
ECMAScript 6에서 드디어 브라우저 기반 Javascript
에 네이티브로 모듈을 지원합니다. type="module"
속성을 추가하여 entry 파일을 로드합니다.
<script type="module" src="entry.js"></script>
Node.js
에서도 12버전부터 ES Module을 지원하기 시작했습니다. package.json
에 type
속성을 추가하여 사용할 수 있습니다.
package.json:
{
// 생략
"type": "module"
// 생략
}
아래와 같이 export
와 import
문을 사용합니다.
// import
import mod from "./modulePath";
// export
export const value = "A value to be exported";
import
문의 경로를 따라 브라우저의 경우 URL로, Node.js
의 경우 파일 시스템의 경로로 해당 모듈을 로드합니다.
댓글남기기