치춘짱베리굿나이스

모듈과 모듈 번들러 본문

Javascript + Typescript/이론과 문법

모듈과 모듈 번들러

치춘 2022. 9. 12. 02:38

모듈과 모듈 번들러

이 글은 웹팩과 웹팩 설정 관련 정리 포스팅을 적기 위한 빌드업이다

모듈

모듈이란

특정 기능을 가진 작은 코드 단위로, 웹팩에서는 하나의 파일을 하나의 모듈로 부른다

계산기를 만든다고 하면, 덧셈을 담당하는 파일, 뺄셈을 담당하는 파일 등 세부 기능 단위로 파일을 분리해서 그 함수를 다른 파일에서 가져다 쓰게 되는데, 각각의 파일이 모듈이라고 할 수 있다

한 파일 내에는 같은 주제의 함수 여러 개가 존재하고, 이를 내보내면 다른 모듈에서 가져와 사용하는 식으로 큰 기능을 구현한다

 

자그마한 테스트용 스크립트 파일이나 간단한 파일 입출력 연산 정도면 파일 한개로 구현이 가능하겠지만, 프로젝트 규모가 커지다 보면 절대 하나의 파일에 모든 기능을 몰아넣을 수 없고, 설렁 넣는다 해도 한 파일에 몇천 자의 코드가 수록되면 가독성이 점점 엉망이 되기 마련이다…

정리하자면 파일별로 관심사를 분리하고 가독성을 높여주는 방법을 ‘모듈화' 라 하고, 비슷한 관심사의 함수들이 모여있는 각각의 파일 또는 라이브러리를 ‘모듈' 이라고 할 수 있다

자바스크립트의 모듈화의 역사

<head>
	<script src="index.js" type="text/javascript"></script>
	<script src="calc.js" type="text/javascript"></script>
	<script src="view.js" type="text/javascript"></script>
</head> <!-- 이렇게 되면 index.js와 calc.js, view.js가 모두 같은 스코프를 공유한다 -->

모듈이 없던 시절, 자바스크립트는 html에서 불러와 사용할 수 있는 만큼 html 코드에 여러 개의 스크립트를 불러와 사용하기도 했으나 이 방법을 사용할 경우, 전역 스코프를 공유한다는 치명적인 단점 때문에 (당시에는 let, const도 없었고 전부 var였다) 자칫하면 이전에 선언한 변수를 덮어쓰거나 하는 문제가 발생할 여지가 있었다

그렇다고 몇천 몇만 줄짜리 파일 한 장에서 작업하고… 리뷰하고… 머지하고… 하기는 좀 그렇기도 하고…

따라서 자바스크립트 커뮤니티에서는 모듈화를 꾀하기 시작했고, 그에 따라 이런저런 노력을 기울였는데, 대표적으로

 

  • CommonJS
    • 아마도 import/export 문을 쓰다가 오류를 겪고 package.json 파일을 건드리면서 한번쯤은 봤을 단어이다
    • 원래의 자바스크립트는 브라우저에서만 돌아갈 것을 상정하고 만들어진 녀석인데, 서버나 데스크톱 어플리케이션 등에도 사용할 수 있도록 좀 더 범용적으로 (Common) 만들기 위해 출범한 그룹이다
    • CommonJS에서는 자바스크립트를 브라우저 밖 환경에서도 사용할 수 있도록 모듈화가 가능케 하는 명세를 제작한다
    • 동기로 동작하지만 비동기를 지원하기는 한다
    • require(), module.exports 가 CommonJS의 모듈 관련 함수들이다
    • Node.js는 CommonJS 표준을 채택하여 사용하고 있다
  • AMD (Asynchronous Module Definition)
    • 리사 수의 그 반도체 회사가 아니다!!!!
    • CommonJS 팀에서 독립하였으며, 그 이유는 ‘범용적으로 자바스크립트를 사용하게 하자!’ 파인 CommonJS와 지향하는 바가 다르기 때문이다 (’브라우저를 중점으로 돌아가도록 하자!’ 파였기 때문)
    • 기본적으로 비동기로 동작하며, 네트워크로 모듈을 내려받는 상황을 상정한다
    • define, require가 AMD의 모듈 관련 함수들으로, define으로 모듈 스코프를 정의하여 사용한다
  • Module
    • 2015년에 공개된 ES6 (ES2015) 에서 드디어 모듈이 표준으로 등재되었다
    • import / export 문으로 간단하게 모듈을 사용하고 정의할 수 있다
    • mjs 확장자를 사용하거나 package.json에서 type: module 지정을 통해 모듈을 사용할 수 있다
    • ES6 표준이기 때문에 ES6을 지원하지 않으면 못 쓴다

가 있다

2015년에 자바스크립트 ES6에서 모듈이 표준으로 등재되면서 대부분은 역사의 뒤안길로 사라져가고 (requireJS도 마지막 업데이트가 2020년이다) commonJS만 명맥을 이어가고 있다

모듈의 필수요소

  • 스코프
    • 각 모듈들은 독립적인 스코프를 가져야 한다
    • 전역 변수를 잘못 사용할 경우 한 모듈의 코드가 다른 모듈의 로직을 오염시킬 수 있다
    • 자바스크립트는 파일마다 독립적인 파일 스코프가 있기 때문에 서버사이드에서는 변수명이나 함수명이 겹쳐도 상관없다
  • 정의
    • 모듈을 export 를 통해 ‘이 파일은 모듈이다' 라고 정의할 수 있어야 한다
  • 사용
    • 다른 모듈에서 require 또는 import 등을 통해 모듈을 불러와 사용할 수 있어야 한다

모듈 번들러

모듈 번들러?

이처럼 모듈화를 통해 파일을 전부 기능별로 분리했다고 치면, 얘네를 어떻게 한번에 컴파일할 지도 관건이다?

번들… 은 험블번들이나 DLC 번들 같은 게임용어 (…) 로 어느정도 익숙해진 단어라, 대충 묶음 내지는 묶음상품같은 뜻이라고 짐작이 가고, 거기에 ~~해주는 사람 / 주체 라는 뜻의 -er 접미어가 붙었으니 번들러는 ‘하나로 묶어주는 사람' 이라는 뜻이라고 생각하면 얼추 맞다

 

실제로 번들러는 여러 개의 분산된 파일을 하나의 파일로 묶어주는 / 포장해주는 역할을 하는 녀석이다

당장 작은 토이 프로젝트만 해도 컴포넌트 / 함수나 훅 / 애셋 파일을 분리하다 보면 디렉토리 구조도 매우 복잡하고 파일 개수도 많아지는데, 이걸 하나의 파일로 뭉쳐준다니 대단한 친구임에 틀림이 없다

번들러가 해주는 역할

  • 위에서 언급했듯, 여러 파일을 하나의 파일로 압축하고, 그 과정에서 불필요한 코드를 줄여 최적화한다
    • 브라우저는 서버에서 스크립트와 마크업 코드를 내려받아 표시해주는 역할을 하므로, 코드의 길이는 최대한 짧고 최적화가 잘 되어 있어야 브라우저의 노동 강도를 줄일 수 있다
    • 주석이나 불필요한 console.log 등 쓸모없는 코드들이 많아질수록 브라우저가 코드를 가져오는 속도도 느려지겠지?
  • 모듈 파일들을 압축해주는 역할을 번들러가 알아서~~ 해주기 때문에 편하게 모듈 단위로 파일을 분리할 수 있다
    • 모듈화는 가독성 좋고 협업이 쉬운 코드로 이어진다 (하나의 몇천 몇만 줄짜리 파일에서 협업하는 것과 여러 파일로 분리하여 각각 맡아 작업하는 것 중 어느게 나을 지 생각해 보자)
  • Internet Explorer (물론 지금은 업데이트가 끊겨 사망 판정이 떨어지긴 했다…) 등 몇몇 브라우저는 ES6 문법을 지원하지 않기 때문에 억지로 ES5 이하의 버전 문법으로 맞춰주어야 하는데, 이 역할도 번들러가 대신 해준다
    • 화살표 함수, letconst, 백틱 (템플릿 리터럴), 객체 리터럴, Promise 등 없으면 상상도 안가는 문법들을 ES5에서는 하나도 사용할 수 없다? (끔찍)
    • ES6으로 맞추어 개발해도 번들러가 알아서 압축 과정에서 ES5로 번역해주니 사용자는 복잡한 고민을 할 필요가 없다

모듈 번들러의 종류

  • Webpack
    • 가장 많이 쓰이는 번들러이다
    • 2012년에 출시되었다
    • 자세한 것은 후술
  • Rollup.js
    • 깃허브를 보니 2015년에 최초로 배포된 것 같다 
    • 코드와 종속 관계에 있는 라이브러리 (코드 또는 모듈에서 가져다가 사용하는 라이브러리) 를 정적으로 분석하고, 그 중 진짜로 코드 내에서 사용하는 것들만 함께 번들링해주므로 용량이 가벼워진다
    • 코드를 동등한 수준으로 올려둔 후 (호이스팅) 한번에 번들링하며 속도가 빠르다
    • 상대경로를 지원하고, import / export를 사용가능케 하는 폴리필이 존재한다
    • 기본적으로 ES6 모듈 형태로 빌드가 가능하므로 파일 분리 (Code Splitting) 시 장점이 두드러지고, 진입점 (Entry Point) 이 여러 개일 때 더욱 빛나므로 많은 라이브러리와 패키지들이 Rollup을 사용한다
    • live-reload (폴더경로에 파일이 하나라도 수정되면, 실행중인 앱도 바로 업데이트해서 보여주는 기능) 위해서 플러그인이 필요하다
  • Browserify
    • 2011년에 출시되었다 (제일 오래되었군)
    • 자바스크립트 파일만 불러올 수 있다 (Sass, png 등 다른 확장자는 플러그인을 사용해야 한다)
    • 기본적으로 빌드 결과를 하나의 파일로 만들어준다
    • UNIX 철학을 고집하여 최소한의 기능만 충실히 구현했기 때문에 상대적으로 좀 느리고, 추가 기능은 외부 플러그인을 사용해야 한다
    • 가장 오래되었지만 후발주자 (Parcel, Webpack, Rollup 등) 에 밀려 잘 쓰이지 않기도 하고, 마지막 업데이트도 2020년에 끊겼다
  • Parcel
    • 2018년에 출시되었다 (비교적 후발주자이다)
    • 웹팩에 비해 빠르다
    • 추가적인 설정이 필요 없다 (zero-configuration) → 웹팩 하나 설정해보겠다고 이렇게 긴 포스팅을 작성할 필요가 거의 없어진다 이것이다 😅
    • 캐싱을 지원하므로 최초 번들링은 조금 느리더라도 두번째 번들링부터 속도가 매우 빨라진다
    • ES6 (import / export), CommonJS 모두에 대해 Tree-shaking을 지원한다
    • 여러 트랜스파일러를 기본 내장하고 있다

처음으로 접한 번들러가 웹팩이라 웹팩을 주로 사용하고 있었는데, 이번에 찾아보니 Rollup.js나 Parcel도 저마다의 장점이 두드러지는 번들러들 같다

복잡한 설정 없이 토이 프로젝트나 예제를 쓱싹 만들고 싶다면 parcel을 이용해서 추가설정 없이 빠르게 앱을 만들고, 웹 앱 대신 어플리케이션을 만들어 가져다 사용하고 싶다면 Rollup을 알아보는 것이 좋겠다


참고자료

NAVER D2

[JS][WEBPACK] 1. 웹팩이란 무엇인가

[Bundler] 번들링Bundling이란? 등장 배경, 종류

롤업과 웹팩의 차이점 (rollup vs webpack)

Browserify와 Webpack

Parcel vs Rollup vs Webpack 비교

Comments