모듈 번들러 톺아보기

wookhyung·2022년 10월 3일
0

톺아보기

목록 보기
4/8

발표 스터디 3주차 주제로 선정한, 모듈 번들러와 빌드 도구에 대한 정리 글입니다.

당근마켓 채용 공고

라인 채용 공고

지금까지 CRA(Create-React-App)를 통해서 개발환경을 세팅했기에, 모듈 번들러를 직접 설정했던 경험이 없었습니다. CRA에서는 이미 만들어진 웹팩 설정 파일이 존재하고, 그 파일이 숨겨져있기 때문입니다. CRA를 이용하여 개발 환경을 구축하는 것은 편하지만, 이후에 커스터마이징이 힘들어집니다.

다음에 CRA가 아닌 환경에서 프로젝트를 진행하게 되면, 모듈 번들러를 다뤄야 할 일이 있을 거로 생각합니다. 채용 공고에서도 웹팩과 같은 모듈 번들러나 Babel 사용 경험이 자격 요건이나 우대 사항에 포함되어 있는 것을 볼 수 있습니다.


🌐 모듈 번들러란 무엇일까요?

먼저, 모듈 번들러가 무엇인지 알기 위해서 단어를 쪼개서, 모듈번들러 라는 단어에 대해서 각각 알아봅시다.

1️⃣ 모듈(Module)이란?

모듈이란, 프로그래밍 관점에서는 특정 기능을 갖는 작은 코드 단위를 의미합니다.

프로그램은 작고 단순한 것에서, 크고 복잡한 것으로 진화해왔습니다. 그 과정에서 스크립트의 크기가 점점 커지고 복잡해지면서 하나의 파일로 관리하기가 힘들어졌습니다.

따라서, 작업의 효율성을 위해 파일을 역할에 따라 분리하는 과정을 거쳤고 이렇게 각각 분리된 파일을 모듈 이라고 합니다.

2️⃣ 번들(Bundle)이란?

번들이란 단어를 어디선가 보셨을 수도 있는데, 저는 상품을 구매할 때, 번들이라는 단어를 봤던 경험이 있습니다. 한국말로 바꿔보면, 묶음 이라는 뜻입니다.

그렇다면, 각각의 의미를 합쳐 정리해보면 모듈 번들러는 각각 분리된 파일들을 묶어주는 도구라고 해석할 수 있습니다.

각각 분리했던 파일들을 하나로 다시 묶는다? 조금 이상하지 않나요?

저는 조금 이상하다고 생각했습니다. 왜냐하면, 각각의 역할에 따라 분리했던 파일들을 왜 다시 묶는 과정을 거치는지 이해가 안됐거든요.


🛠 자바스크립트에서 모듈화는 어떻게 할까요?

이는 예전으로 조금 돌아가서, 전통적인 자바스크립트 개발 방식을 되짚어봅시다.

자바스크립트 파일을 사용하기 위해서는 다음과 같이, HTML에서 <script> 태그를 이용하여 로드했습니다.

<html>
  <head>
    <script src="./hello.js"></script>
    <script src="./world.js"></script>
  </head>
  <body>
    <div id="root"></div>
    <script>
      document.querySelector('#root').innerHTML = word;
    </script>
  </body>
</html>

이러한 상황에서 만약, hello.js와 world.js에 같은 이름의 변수가 있다고 가정해봅시다.

// hello.js
var word = 'Hello';
// world.js
var word = 'World';

✅ 여기서, 과연 화면에는 어떤 word의 값이 나타날까요?

자바스크립트 파일이 순서대로 로드되면서, 같은 이름의 변수인 word가 마지막에 불러온 world.js에 있는 변수로 덮어씌워집니다. 즉, 화면에는 World가 출력되겠죠.

지금 같이 단순한 상황에서는 금방 문제점을 찾아 해결할 수 있겠지만, 자바스크립트 파일이 100개, 1000개가 된다면 어떨까요? 전역 스코프를 오염시키고, 예측하기 어려우며 오류가 발생하기 쉬운 환경이 될 것입니다.

이러한 방식은 그냥 코드를 분리한 것이지, 사실상 하나의 파일에 모든 코드가 들어있는 것과 다름이 없습니다.자바스크립트에는 다른 언어처럼 모듈 시스템이 존재하지 않았기 때문에, 개발자들은 이를 해결할 수 있는 방법을 모색합니다.

이런 문제들을 어떻게 해결할 수 있을까요?

1. 클로저의 원리를 이용하여 IIFE(즉시실행 함수)를 통해 모듈 패턴을 구현한다.

var wordIIFE = (function () {
  var word = 'hello';

  return {
    getWord: function () {
      return word;
    },
  };
})();

console.log(wordIIFE.getWord()); // hello
console.log(word); // ReferenceError

다음과 같이, 클로저의 원리를 이용하여 IIFE를 통해 모듈 패턴을 구현하면 외부로부터 독립적인 스코프를 만들어서 기존의 문제를 해결할 수도 있었습니다.

2. Common JS, AMD, UMD와 같은 모듈 시스템을 이용한다.

자바스크립트가 탄생한 이후부터 개발자들은 자바스크립트가 브라우저 뿐만 아니라, 더 범용적으로 사용할 수 있기를 원했습니다. 특히, 서버 사이드에서 사용하려는 움직임이 나타납니다.

브라우저를 넘어 범용적으로 사용하기에 앞서, 해결해야 될 문제점이 많았습니다. 서로 호환되는 표준 라이브러리가 없으며, 다른 모듈을 삽입하는 표준적인 방법이 없다는 등의 문제점이 있었습니다.

이를 해결하기 위해서는 결국 모듈화 가 필요했으며, 2009년 모듈화 명세를 만든 대표적인 그룹 중 하나인 CommonJS가 등장하며 모듈을 어떻게 정의하고, 어떻게 사용할 것인가에 대해 정의합니다.

CommonJS에서 모듈화는 다음과 같이 세 부분으로 이루어집니다.

  • 스코프(Scope): 모든 모듈은 자신만의 독립적인 실행 영역이 있어야 한다.
  • 정의(Definition): 모듈 정의는 exports 객체를 이용한다.
  • 사용(Usage): 모듈 사용은 require 함수를 이용한다.

또한, 모든 파일이 로컬에 존재하여 바로 불러올 수 있음을 전제로 하며 동기적으로 동작한다는 특징을 가지고 있습니다.

module.exports = {};
require('./a.js');

자바스크립트 개발자라면, 다음과 같은 문법을 보신 적이 있을 것 같은데 Node.js에서 CommonJS를 사용하고 있습니다.

AMD와 UMD같은 자바스크립트 표준을 만들기 위해서 탄생한 그룹도 있습니다. AMD는 CommonJS와 함께 비동기 상황에서 모듈을 사용하는 것에 대해 논의했으나, CommonJS 측은 자바스크립트를 브라우저 밖에 사용하고 싶었고 AMD는 브라우저 내의 실행에 중점을 두고 비동기 모듈 사용을 원했기 떄문에 합의점을 찾지 못하고 독립한 그룹입니다. CommonJS와 AMD를 모두 지원해야 하는 경우에는, UMD를 사용할 수도 있습니다.

3. script type="module"을 이용한다.

script 태그에서 type="module"을 이용하여 해결할 수도 있지만, 인터넷 익스플로러와 같은 몇 브라우저들은 이를 지원하지 않습니다.


🚀 표준 모듈 시스템의 등장(import/export)

그러던 와중, ES6부터 드디어 자바스크립트에 정식적으로 모듈 시스템(import/export)이 도입됩니다.

import App from './App.js";
export default App;

표준 모듈 시스템이 등장한 것은 좋았으나, 아직 해결해야 할 문제점이 남아있었습니다. 모든 파일들을 네트워크 통신을 통해서 가져오는데, 대부분의 현대 브라우저에서는 각 호스트 당 한 번에 요청을 보낼 수 있는 횟수가 6회로 제한되어 있다는 점입니다. 즉, 분리된 모든 파일을 한 번에 가져올 수 없습니다.

먼저, 6개의 파일을 가져오는 작업이 끝난 뒤에 다음 6개의 파일을 가져오게 됩니다. 이렇게 나눠서 요청을 보내게 되면, 브라우저의 연결 제한에 따라 대기 시간이 발생하게 되고 이는 성능 저하로 이어질 수 있습니다.

이제 글 처음에서 언급했던, 분리된 파일들을 왜 다시 묶는지에 대한 의문이 조금 풀리셨나요?

드디어, 분리된 파일들을 다시 묶어줄 '모듈 번들러'가 필요해졌습니다.


⚙️ 모듈 번들러 - Webpack과 Babel

대표적인 모듈 번들러인 웹팩에서는 웹 애플리케이션을 구성하는 모든 자원들(HTML, CSS, JS, Image, ...)을 각각의 모듈로 보고, 이를 묶어서 합쳐진 하나의 결과물을 만들어줍니다.

웹팩을 사용하기 위해서는 mode, entry와 output에 대한 설정이 필수적입니다.

module.exports = {
  mode: 'development',
  entry: './src/index.tsx',
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'bundle.js',
  },
}

📖 Mode

웹팩에서는 mode 속성을 통하여 개발 또는 프로덕션 같은 모드를 정해서 각 모드에 맞는 최적화 옵션들을 사용할 수 있습니다.

📖 Entry와 Output

웹팩은 모듈이 시작되는 부분부터 의존성에 따라 필요한 모듈들을 묶어서 결과물을 만듭니다. 일반적으로 entry는 index.js가 됩니다.

output은 번들링된 결과물을 저장할 위치입니다. 일반적으로 output은 dist 혹은 build라는 이름의 폴더를 사용합니다.

그 외에 Loader나 Plugin 같은 옵션도 있습니다.

📖 Loader

로더는 자바스크립트 파일이 아닌 웹 자원(HTML, CSS, 이미지, 폰트)들을 변환할 수 있도록 도와주는 도구입니다.

주로 자바스크립트에서 CSS 파일을 import 할 수 있게 해주는 'css-loader', css-loader를 통해서 import된 CSS를 HTML에 넣어 렌더링 시 적용될 수 있게 해주는 'style-loader', 이미지 같은 파일들을 import 할 수 있게 해주는 'file-loader'가 많이 사용됩니다.

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
      },
      {
        test: /\.(?:ico|gif|png|jpg|jpeg)$/i,
        use: 'file-loader',
      },
    ],
  },
}

📖 Plugins

플러그인은 번들링된 결과물을 어떻게 처리할지 도와주는 역할을 합니다.

자주 사용되는 플러그인은 'html-webpack-plugin'인데, HTML 파일에 빌드한 결과물을 자동으로 주입해주는 역할을 하게 됩니다.

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  plugins: [
    new HtmlWebpackPlugin({ template: './public/index.html' }),
  ],
};

마지막으로, 트랜스파일러인 바벨(Babel)에 대해서도 알아봅시다.

바벨은 크로스 브라우징 문제를 해결하기 위해서 등장했습니다.

크로스 브라우징이란, 모든 브라우저에서 깨지지 않고 의도한 대로 올바르게(호환성) 나오게 하는 작업을 말합니다.

자바스크립트가 빠르게 발전하면서 최신 브라우저 조차 지원하지 않는 문법과 기술들이 등장하고 있습니다. 또한, 옛날 브라우저를 사용하는 유저도 있기 때문에 이를 고려하여 브라우저 스펙과 상관없이 모든 브라우저에서 같은 화면을 보여줄 수 있어야 합니다.

바벨은 우리가 원하는 대로 최신 스펙을 사용하여 자바스크립트 코드를 작성하더라도, 모든 브라우저에서 동일하게 동작할 수 있는 코드로 변환시켜줍니다. 최신 스펙의 자바스크립트 코드를 이전 자바스크립트 코드를 바꾸는 것 뿐만 아니라 타입스크립트나, 리액트의 JSX 문법을 자바스크립트로 변환할 수 있습니다.

바벨은 다음과 같은 세 가지 과정을 거쳐 코드를 변환시켜줍니다.

  1. 파싱(Parsing): 입력된 코드로부터 AST(abstract syntax tree)를 생성합니다.
  2. 변환(Transforming): AST를 원하는 형태로 변환합니다.
  3. 출력 (Printing): AST를 코드로 출력합니다.


🧐 생각 정리

지금까지 자바스크립트의 모듈 표준화가 어떻게 되었는지, 모듈 번들러가 왜 필요한지, 바벨이 무엇인지에 대해서 알아보았습니다. 글의 서두에도 적혀있지만, Webpack이나 Rollup 등의 모듈 번들러나 Babel과 같은 트랜스파일러에 대한 경험은 채용 공고에서도 강조하고 있는 내용이며, 현대의 웹을 개발한다면 꼭 알아야 될 내용이라고 생각합니다.

CRA 환경에서 프로젝트를 진행하면서, 어떻게 타입스크립트나 JSX 코드를 작성했는데 브라우저에서는 자바스크립트 코드로 변환되어 실행되는 것인지, 어떻게 ES6 이상의 문법을 지원하지 않는 브라우저에서도 코드가 정상적으로 실행이 되는지 정확하게 알지 못했는데 이번 포스팅을 통해서 어느정도 정리가 된 것 같습니다.

최근 진행 중인 프로젝트에서는 CRA 없이 직접 개발환경을 구축해보고 있습니다. 직접 사용해보면서, 웹팩과 바벨에 대해서 더 이해하는 시간을 가져보겠습니다. 감사합니다.

Reference

https://learn.microsoft.com/ko-kr/aspnet/mvc/overview/performance/bundling-and-minification
https://joshua1988.github.io/webpack-guide/webpack/what-is-webpack.html#%EB%AA%A8%EB%93%88%EC%9D%B4%EB%9E%80
https://youtu.be/bRC9RT9JP18
https://velog.io/@uiseop/JS-바벨Babel과-폴리필Polyfill
https://babeljs.io/
https://youtu.be/9b89f21Sizs
https://velog.io/@dea8307/모듈-번들러-webpack과-babel
https://chicori3.tistory.com/entry/IFFE%EC%99%80-%EB%AA%A8%EB%93%88
https://d2.naver.com/helloworld/12864
https://velog.io/@leobit/CommonJS

profile
Front-end Developer

0개의 댓글