본문 바로가기
📍 DEV/JS & TS

모듈과 모듈 번들러란?! (feat. Webpack)

by briee 2021. 7. 27.

react에서 create-react-app을 사용하면 리액트에 집중할 수 있지만 웹팩, 바벨 등 번들링 도구들이 어떻게 동작하는지는 잘 모르고 넘어가게 된다. 하지만 프론트엔드 개발 환경을 이해하기 위해선 꼭 알아야 한다고 생각하기 때문에 정리를 해보고자 한다. 📝❗️

 


 

모듈(Module)이란?!

한줄로 정의하자면 모듈은 파일 하나하나, 특정 기능을 갖는 작은 코드 단위를 의미한다. 우리가 개발을 하면서 규모가 커지면 언젠가 파일을 여러 개로 분리해야 하는 시점이 온다. 이때 분리된 파일 각각을 모듈이라고 부르는 것이다. 목적에 따라, 기능별로 여러 개의 파일로 분리해서 관리할 수 있으며 모듈로 분리하는 과정을 모듈화라고 한다. 그렇다면 모듈을 왜 사용해야 하는 걸까?!

 

 

모듈이 필요한 이유 : 

브라우저 내에서 자바스크립트는 여러 파일로 분리해도 하나의 파일 안에 있는 것처럼 전역(window)을 공유한다. 그래서 어떤 파일을 먼저 실행할지가 중요해진다. 코드로 보면 더 쉽게 이해가 된다.  

 

//app.js
let title = 'Hello';

function print(value) {
  console.log(value);
}


//main.js
let title = 'Brie';

function print(value) {
  console.log(value);
}

 

//index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Module Practice</title>
</head>
<body>
  <script src="app.js"></script>
  <script src="main.js"></script>
</body>
</html>

 

이렇게  app.js와 main.js로 파일을 나눴지만, 같은 변수가 선언된다면 app.js를 불러올 때는 Hello지만 main,js가 호출되면 title은 Brie가 된다. 이렇게 개발하다 규모가 커지면 오류를 찾기 힘들고, 하나의 파일에 작성하는 것과 다른 것이 없어진다. 그래서 이렇게 파일 단위로 변수를 관리하기 위해서는 모듈이 필요한 것이다. 

또한 똑같은 코드를 반복하지 않고, 모듈로 분리시켜서 필요할 때마다 사용할 수 있고(재사용성),  잘 분리되어 있다면 의존성을 그만큼 줄일 수 있기 때문에 기능을 개선하거나 수정할 때 훨씬 편하게 사용할 수 있다(유지보수성).

 

 

모듈의 동작 : 

각각의 모듈은 독립적인 스코프를 가져야하고, 정의된 모듈을 가져다 사용할 수 있어야 한다. 

 

✔️ export, import를 사용하면 모듈을 내보내고 가져오는 것이 가능하다. 

//math.js
export function sum(a, b) {
  return a + b;
}

//app.js
import * as math from './math.js';
math.sum(1, 2);

 

✔️ html파일 내에서 script 태그에 type속성을 module이란 값으로 지정해 모듈을 사용할 수도 있다.

<script type="module" src="app.js"></script>

 

 

이러한 모듈은 ES2015에서야 모듈화를 지원하는 표준 문법이 등장해서 이 표준화된 모듈 문법을 사용할 수 있게 되었다. 그래서 표준 문법 등장 전에는 AMD, CommonJS와 같은 라이브러리를 사용했었다. 

AMD – 가장 오래된 모듈 시스템 중 하나로 require.js라는 라이브러리를 통해 처음 개발되었다.
CommonJS – 2009년 Node.js 서버를 위해 만들어진 모듈 시스템이다. 현재 Node.js에서 사용하고 있다.
UMD – AMD와 CommonJS와 같은 다양한 모듈 시스템을 함께 사용하기 위해 만들어졌다.

 

 

하지만 브라우저 환경에서 모듈을 단독으로 사용하는 경우는 흔치 않다. 대부분 웹팩과 같은 번들러를 통해 모듈을 번들링해 사용한다. 그렇다면 모듈 번들러란 무엇이고, 왜 사용하는 것일까?! 

 

 


 

모듈 번들러(Module bundler) :

아래 그림과 같이 웹 애플리케이션을 구성하는 몇십, 몇백 개의 자원들을 하나로 병합 및 압축해주는 동작을 모듈 번들링이라하며 번들링을 할 수 있게 합쳐주는 도구가 번들러이다. 즉, 모듈 번들러는 여러 개의 파일을 하나의 파일로 합쳐주는 역할을 한다.

 

 

 

모듈 번들러가 필요한 이유 :

모듈을 이용하는 것은 좋지만 모든 파일을 네트워크 통신을 통해 가져와야 한다. 파일 하나하나 요청하고 가져온다면 로딩 속도는 늦어질 것이다. 또한 웹 서비스를 개발하고, 배포할 때 HTML, CSS, JS압축, 이미지 압축, CSS 전처리기 변환과 같은 작업들을 해야 했다. 그래서 로딩 속도 개선과 이러한 일들을 자동화해주는 도구들이 필요했고, Grunt와 Gulp 같은 도구들이 등장했다. 

 

 

웹팩 (Webpack) : 

웹팩은 모듈 번들러 중 최신 프론트엔드에서 가장 많이 사용되고 있다. 엔트리 포인트를 시작으로 연결되어 었는 모든 모듈을 하나로 합쳐서 결과물을 만드는 것이 웹팩의 역할이다. 자바스크립트 모듈 뿐만 아니라 스타일시트, 이미지 파일까지도 모듈로 제공해 주기 때문에 일관적으로 개발할 수 있다. 웹팩을 사용할 떄 중요한 속성이 4가지가 있다. 바로 entry, output, loader, plugin이다.  (여기서 어떻게 사용하는지까지 포스팅하면 너무 길어지고 주제가 합쳐질 것 같아 필수 개념만 간단히 정리하겠다. 자세히 알고 싶다면, 밑의 참고 블로그를 보면 좋을 것 같다.)

 

 

Entry (엔트리) / Output(아웃풋) :

웹팩은 하나의 시작점(entry point)으로부터 의존적인 모듈을 전부 찾아내서 하나의 결과물을 만들어낸다. 그렇기 때문에 entry 속성은 웹팩에서 웹 자원을 변환하기 위해 필요한 최초의 진입점이자 자바스크립트 파일 경로이다. 밑의 세 가지 옵션을 사용하면 코드를 묶을 수 있다.

 

--mode : 웹팩의 실행모드를 의미, 개발 또는 프로덕션과 같은 모드

--entry : 모듈이 시작되는 부분, 시작점 경로를 지정

--output: 번들링된 파일들을 저장할 위치, 일반적으로 dist, build라는 이름의 폴더를 사용한다. 

 

 

Loader(로더) : 

로더는 웹팩이 자바스크립트 파일이 아닌 웹 자원(HTML, CSS, 이미지, 폰트 등)들을 변환할 수 있도록 도와준다. 앞서 말했듯 웹팩은 모든 파일을 전부 모듈로 보기 때문에 import 구문을 사용하면, 자바스크립트 코드 안으로 이미지, 폰트 등을 가져올 수 있는 것은 웹팩의 로더 덕분이다. 로더는 타입스크립트같은 다른 언어를 자바스크립트 문법으로 변환해주거나, 이미지를 data URL형식의 문자열로 변환한다. 

또한 CSS파일을 자바스크립트에서 직접 로딩할 수 있도록 해준다. 자주 사용되는 로더로는 css-loader, style-loader, file-loader 등이 있다.



Plugin (플러그인) :

플러그인은 웹팩의 기본적인 동작에 추가적인 기능을 제공한다. 로더는 파일을 해석하고 변환하는 과정에 관여하고, 플러그인은 해당 결과물의 형태를 바꾸는 역할을 한다. 예를 들어, 번들된 자바스크립트를 난독화 한다거나 특정 텍스트를 추출하는 용도로 사용한다.

 

 

마무리하며 : 

앞서 말했듯이 create-react-app에서는 이미 기본 설정이 되어있는데 프로젝트 디렉토리를 간결하게 하기 위해서 웹팩 설정이나 자세한 동작을 명시한 script 폴더를 숨겨놓는다고 한다. npm run eject를 통해 숨겨진 폴더를 표시되게 만들 수는 있지만 eject 전으로 돌아갈 수 없어서 말리는 자료들이 많길래... 너무 떨려서 시도해보지는 못했다!🤭 대신 나는 참고에 넣어놓은 웹팩 핸드북을 보며 처음부터 설정하는 튜토리얼이 간단히 있어 따라 해 보았다. 궁금하다면 구글링 해서 eject 해서 설명한 글들이 있어 참고해서 보면 좋을 것 같다!

 

 

참고: