시작하는 중
모듈, 번들러, 모듈 번들러의 개념과 등장한 이유 본문
글의 목적
웹팩 공부 전에 모듈, 번들러, 모듈 번들러의 개념과 등장한 이유에 대해 살펴보려고 한다.
ES6 이전의 자바스크립트
모듈이 있기 전의 JS는 다음과 같이 파일들을 관리했다.
<script src="/A.js"></script>
이런식으로 관리하였고, A라는 JS 파일을 분할하기 위해서는
<script src="/A1.js"></script>
<script src="/A2.js"></script>
꽤나 그럴 듯 해보이지만, 스크립트를 로드한 브라우저 자체의 전역 컨텍스트에서 각 모듈 간의 충돌이 발생했다.
예를 들어, A1.js에서 "a"라는 변수명을 사용하고 있었고, A2.js에서도 "a"를 사용하고 있다면, 충돌이 일어났다.
// A1.js
var a = 1;
// A2.js
var a = 2;
즉, 모듈 간의 스코프가 구분되지 않았고, 다른 파일을 오염시키는 경우가 발생했다. 그래서 다른 파일이더라도 오염을 방지하기 위해서 변수명을 모두 다르게 만들고, 자바스크립트 파일도 로드하는 순서도 신경써야했다.
JS 모듈화에 힘쓴 CommonJS
구글이 2008년 V8 엔진을 발표하며, 빠른 자바스크립트 엔진이 나왔고 이는 브라우저 밖에서도 사용하기에 충분했다.
이듬해 2009년, V8 엔진을 기반으로 브라우저 밖에서도 사용하고자 하는 사람들이 표준을 만들자는 사람들이 모여 CommonJS 그룹이 만들어졌고 CommonJS 표준을 발표했다.
CommonJS는 자바스크립트를 범용적인 언어로 사용하기 위해서는 체계가 필요하다고 하였다. 이 체계를 위해서는 모듈화가 필요하였다. 그래서 CommonJS는 모듈화를 어떻게 정의하고, 어떻게 사용할 것인가에 대해 자세히 표준을 정했다.
CommonJS의 모듈화는 다음과 같다.
모듈화는 아래와 같이 세 부분으로 이루어진다.
스코프(Scope): 모든 모듈은 자신만의 독립적인 실행 영역이 있어야 한다.
정의(Definition): 모듈 정의는 exports 객체를 이용한다.
사용(Usage): 모듈 사용은 require 함수를 이용한다.
출처 : 네이버 기술블로그 JavaScript 표준을 위한 움직임
스코프는 지역변수와 전역변수를 분리하는 분기점이 되어주었고.
정의의 exports와 사용의 require는 모듈화의 핵심이 되었다.
정말 좋은 방식이지만, 2개의 대표적 문제점이 있었다.
1. 브라우저에서는 필요한 모듈을 모두 내려받을 때까지 아무것도 할 수 없게 되었다.
브라우저는 결국 동적으로 script 태그를 통해 JS 파일을 불러오기로 하였다.
2. 브라우저에는 스코프의 개념이 없었다.
CommonJS는 모듈 전송 포맷이라는 개념을 정의했고 이를 통해서 서버 모듈을 비동기적으로 로드할 수 있게 되었다.
AMD
CommonJS처럼 브라우저 내에서도 자바스크립트 모듈화를 위한 표준을 만들기 위해 만들어진 그룹이 AMD이다.
AMD는 브라우저의 비동기 모듈에 대한 표준안을 다루고 있다. 대표적으로 define 함수가 있는데, 의존하는 모듈을 명시적으로 선언하고 클로저의 개념과 함께 필요한 모듈을 비동기적으로 불러온다.
CommonJS + AMD를 호환할 수 있는 UMD
CommonJS와 AMD의 모듈을 모두 호환시킬 수 있는 패턴이다. 조건문을 통해서 CommonJS식인지 AMD식인지 분기하면서 모두 호환할 수 있었고 window 객체를 통해서 전역적으로도 접근할 수 있었다.
그러다 나온 ES6
이렇게 개발자들이 고생했던 것들을 겪고 나니, ES6의 등장이 너무 웅장하다 생각한다.
자바스크립트는 모듈 시스템의 부재로 인해 나온 개념들인 CommonJS 표준, AMD 표준, UMD 패턴 등등을 해결하고자 하였다.
자바스크립트는 ES6 Moduile이라는 자바스크립트 자체의 표준 모듈 시스템을 명세하였다. ES6 Module은 이전의 문제점들을 바탕으로 많은 노력을 기울였다. 동기/비동기 로드를 모두 지원하도록 나왔고 문법도 간단하게 나왔다.
하지만 또 다른 문제가 생겼다. ES6에서 모듈이라는 개념을 표준 문법으로 정의했고 이를 통해 개발하기 시작했으나, 브라우저에서 지원하지 않는 경우가 많았다. 크롬의 경우에도 2017년이 되어서야 ES6의 모듈화된 것을 컴파일하여 직접 사용할 수 있게 되었다고 한다. 크롬 61 베타 버전 안내
ES6는 2015년에 나왔는데 간극이 크기도 하고, 실제 회사들의 프로덕트는 이런 실험적이거나 갓 나온 기능보다는 안정적인 방향을 택할 것이다. 이를 제외하더라도, ES6 발표 후 적어도 최소 2년간의 공백이 존재하는데, 이 사이의 개발자들은 ES6의 편리한 기능들을 사용하길 원했을 것이다.
그래서 CommonJS, AMD, ES6 Module까지 전부 지원하는 형태의 모듈 로더가 나오는 상황도 생겼다.
하지만, 이런 서드파티 라이브러리에 의존하는 것은 기분 좋은 일은 아니었을 것이다. 그래서 ES6 문법으로 만들어진 JS 코드를 그 이전의 코드로 변환시켜주는 JS 트랜스파일러를 생각하게 된다.
대표적인 트랜스파일러가 바로 바벨이다.
바벨
가장 유명한 것으로는 바벨(Babel)이 있습니다. 개발할 때에는 최신 JavaScript 문법을 사용하되, 바벨로 컴파일을 하고 난 후에는 같은 동작을 하면서 구형 브라우저 호환이 되는 JavaScript 코드로 변환 되니 개발자들은 호환성 걱정 없이 생산성 높은 최신 문법을 사용할 수 있게 되었습니다.
출처 : 지그재그 포스트 - JS번들러
번들러
번들러는 여러 JS 파일을 하나의 파일로 패키징하는 번들링이라는 과정을 진행해주는 도구이다.
근데 사실 번들러라는 것은 모듈 표준 이전부터 필요했다.
모듈이라는 개념을 떠나서 번들링 자체만 생각해보면, html의 script에 5개의 자바스크립트 파일을 받아와야한다면 두개의 방법이 있다.
<script src="/1.js"></script>
<script src="/2.js"></script>
<script src="/3.js"></script>
<script src="/4.js"></script>
<script src="/5.js"></script>
같이 5개를 따로 받아오거나,
<script src="/12345.js"></script>
같이 한번에 받아오는 방식이다. 생각해본다면, 전자의 경우에는 5번의 HTTP 요청이 이뤄지지만 후자의 경우에는 1번의 HTTP 요청만 있으면 된다. 이에 대한 필요성은 CommonJS 표준 발표 이전에도 필요성이 있었다.
모듈 번들러
번들러는 크게 세 가지 이유로 사용하는데, 아직까지 모든 브라우저가 모듈 시스템을 완전하게 지원하지 않고, 코드의 종속성 관계를 관리하는 데 도움이 되며, 종속성 순서, 이미지, CSS 에셋 등을 로드하는 데 도움이 되기 때문 입니다.
출처 : 지그재그 포스트 - JS번들러
번들러가 처음 만들어진 이유는, CommonJS식 모듈을 브라우저 상에서 실행시키기 위해 번들링하는 것이었다. CommonJS로 만들어진 JS 파일을 브라우저 상에서 실행시키기 위해서 여러 모듈들을 묶는 모듈 번들러가 필요했다. 이 번들러는 바로 Browserify이다.
하지만, 웹 애플리케이션이 커지고 복잡해짐에 따라, 이제는 불필요한 코드를 제거하고 종속성 순서를 정리하는 최적화 작업까지 필요하게 되었다. 그러면서 더욱 고급 번들러가 요구되었다.
+ 태스크 러너
태스크 러너는 테스트, lint, 컴파일 등을 자동화 할 수 있는 도구이다. V8 엔진으로 인해서 자바스크립트 엔진이 향상됨에 따라 자바스크립트가 할 수 있는 일과 해야할 일이 늘어남에 따라, 태스크 러너가 필요해졌다. 이 때 등장하는 것이 Grunt와 Gulp이다.
이들은 플러그인을 사용하여 번들링 기능을 제공했는데 보다 전문적인 기능을 가진 고급 번들러가 필요했다.
webpack!
누구나 한번쯤 들어봤을 webpack이다. 왜 갑자기 Browserify는 사라졌냐?하면
browserify는 2011년에 등장했고 웹팩은 2012년에 등장했다.
Browserify가 더 인기있었지만 ES6문법을 지원하지 않은 반면에 웹팩은 ES6 모듈 문법을 지원하였고, 커뮤니티적으로 지원을 받으며 가장 인기있는 모듈 번들러 중 하나가 되었다.
또한, 코드 분할 같은 고급 에셋 처리 기능을 Browserify는 지원하지 않았고 태스크 러너의 기능까지 웹팩이 갖게 되었다. 때문에 고급 번들러인 웹팩이 떠오른 것!
웹팩에 대한 것은 추후에 자세히 살펴볼 예정이다.
정리
- 모듈화에 대한 필요성이 대두되던 중 V8 엔진이 발표되며 자바스크립트의 성능이 향상되었다.
- 때문에 자바스크립트는 브라우저 밖에서도 쓰일만한 충분한 성능을 지니게 되었고, 서버사이드에서 사용하려는 단체가 생겼다.
- 이 단체는 CommonJS이며 CommonJS의 표준을 통해 모듈화하는 움직임이 생겼다.
- 여기에 더하여 AMD도 생겨서 브라우저 단에서도 모듈에 대한 개념이 생겨, 전역 스코프에 대한 개념과 비동기 모듈을 사용할 수 있게 되었다.
- CommonJS는 브라우저에서 사용할 수 없는 개념이었기에, 모듈 번들러가 등장하여 CommonJS가 분할한 모듈 파일을 번들링하여 하나의 파일로 사용할 수 있도록 한 것이 등장했고 이는 Browserify이다.
- 여기에 더하여, 자바스크립트에서 할 수 있는 일들이 많아짐에 따라, 테스트, lint, 컴파일 등을 자동으로 해주는 태스크 러너(Grunt와 Gulp)가 등장하였다.
- 태스크 러너들은 플러그인으로써 번들링을 지원했다. 하지만, 더더욱 복잡해지는 자바스크립트 코드에 대해 최적화까지 해주는 전문적인 고급 번들러가 필요해졌다.
- 웹팩이 등장하고, 고급 에셋 처리 기능들을 지원하면서 인기가 있어졌고, ES6 발표 후 웹팩에서 ES6 모듈 처리까지 지원하며 성장하였고, 태스크 러너의 기능을 갖게 되면서 웹팩이 이들을 대체하였다.
reference
https://wormwlrm.github.io/2020/08/12/History-of-JavaScript-Modules-and-Bundlers.html
https://d2.naver.com/helloworld/12864
https://itchallenger.tistory.com/800
'자바스크립트' 카테고리의 다른 글
웹팩 webpack 설정 알아보기 (0) | 2023.05.24 |
---|---|
함수형 프로그래밍 (0) | 2023.05.15 |
자바스크립트 iterator와 iterable (0) | 2023.04.07 |
업로드한 이미지 미리보기 (0) | 2023.03.29 |
드래그 앤 드롭 업로드 (0) | 2023.03.28 |