개발 기록 남기기✍️

[Deep Dive] 49장 - Babel과 Webpack을 이용한 ES6+/ES.NEXT 개발 환경 구축 본문

Front-End/숨참고 Deep Dive🏊‍♀️

[Deep Dive] 49장 - Babel과 Webpack을 이용한 ES6+/ES.NEXT 개발 환경 구축

너해동물원 2023. 2. 26. 10:00

이 카테고리는 모던 자바스크립트 Deep Dive 책을 공부하며 정리한 글을 포스팅하는 공간입니다.


ES6+와 ES.NEXT의 최신 ECMAScript 사양을 사용하여 프로젝트를 진행하려면 최신 사양으로 작성된 코드를 경우에 따라 구형 브라우저에서 문제 없이 동작시키기 위한 개발 환경을 구축하는 것이 필요하다.

 

또한 대부분의 프로젝트가 모듈을 사용하므로 모듈 로더도 필요하다. ESM은 대부분의 모던 브라우저에서 사용할 수 있다. 하지만 다음과 같은 이유로 아직까지는 ESM보다는 별도의 모듈 로더를 사용하는 것이 일반적이다.

  • IE를 포함한 구형 브라우저에서는 ESM을 지원하지 않는다.
  • ESM을 사용하더라도 트랜스파일링이나 번들링이 필요한 것은 변함이 없다.
  • ESM이 아직 지원하지 않는 기능(bare import 등)이 있고 점차 해결되고는 있지만 아직 몇 가지 이슈가 존재한다.

 

 

 

49.1 Babel

Babel은 ES6+/ES.NEXT로 구현된 최신 사양의 소스코드를 구형 브라우저에서도 동작하는 ES5 사양의 소스코드로 변환(트랜스파일링)할 수 있다.

 

 

Babel 설치

# 프로젝트 폴더 생성
$ mkdir esnext=project && cd esnext-project
# package.json 생성
$ npm init -y
# babel-core, babel-cli 설치
$ npm install --save-dev @babel/core @babel/cli

 

 

Babel 프리셋 설치와 babel.config.json 설정 파일 작성

Babel을 사용하려면 @babel/preset-env를 설치해야 한다. @babel/preset-env는 함께 사용되어야 하는 Babel 플러그인을 모아둔 것으로 Babel 프리셋이라고 부른다. Babel이 제공하는 공식 Babel 프리셋은 다음과 같다.

  • @babel/preset-env
  • @babel/preset-flow
  • @babel/preset-react
  • @babel/preset-typescript
# @babel/preset-env 설치
$ npm install --save-dev @babel/preset-env

 

설치가 완료되면 프로젝트 루트 폴더에 babel.config.json 설정 파일을 생성하고 다음과 같이 작성한다. 지금 설치한 @babel/preset-env를 사용하겠다는 의미다.

{
	"presets" : ["@babel/preset-env"]
}

 

 

트랜스파일링

package.json 파일에 script를 추가한다.

"scripts" : {
	"build" : "babel src/js -w -d dist/js"
}

npm scripts의 build는 src/js 폴더(타깃 폴더)에 있는 모든 자바스크립트 파일들을 트랜스파일링 후, 그 결과물을 dist/js 폴더에 저장한다.

  • -w : 타깃 폴더에 있는 모든 자바스크립트 파일들의 변경을 감지하여 자동으로 트랜스파일한다. (--watch 옵션의 축약형)
  • -d : 트랜스파일링된 결과물이 저장될 폴더를 지정한다. 만약 저장된 폴더가 존재하지 않으면 자동 생성된다. (--out-dir 옵션의 축약형)

 

49.2 Webpack

Webpack은 의존 관계에 있는 자바스크립트, CSS, 이미지 등의 리소스들을 하나(또는 여러개)의 파일로 번들링 하는 모듈 번들러다.

Webpack을 사용하면 의존 모듈이 하나의 파일로 번들링되므로 별도의 모듈 로더가 필요 없다.
그리고 여러 개의 자바스크립트 파일을 하나로 번들링하므로 HTML 파일에서 script 태그로 여러 개의 자바스크립트 파일을 로드해야 하는 번거로움도 사라진다.

 

webpack.config.js 설정 파일 작성

webpack.config.js는 WEbpack이 실행될 때 참조하는 설정 파일이다.
프로젝트 루트 폴더에 webpack.config.js 파일을 생성하고 다음과 같이 작성한다.

const path = require('path');

module.exports = {
  // entry file
  entry: './src/js/main.js',
  //번들링된 js파일의 이름(filename)과 지정될 경로(path)를 지정
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'js/bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        include: [
          path.resolve(__dirname, 'src/js')
          ],
        exclude: /node_module/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
            plugins: ['@babel/plugin-proposal-class-properties']
          }
        }
      }
      }
      },
  devtool: 'source-map',
  mode: 'development'
};

Webpack을 실행하여 트랜스파일링 및 번들링을 실행해보자.

트랜스파일링은 Babel이 소행하고 번들링은 WEbpack이 수행한다. Webpack을 실행한 결과, dist/js 폴더에 bundle.js가 생성된다. 이 파일은 main.js, lib.js 모듈이 하나로 번들링된 결과물이다.

 

 

babel-polyfill 설치

Babel을 사용하여 ES6+/ES.NEXT 사양의 소스코드를 ES5 사양의 소스코드로 트랜스파일링해도 브라우저가 지원하지 않는 코드가 남아 있을 수 있다.
예를 들어, ES6에서 추가된 Promise, Object.assign, Array.from등은 ES5 사양으로 트랜스파일링 해도 ES5 사양에 대체할 기능이 없기 때문에 트랜스파일링되지 못하고 그대로 남는다.
src/js/main.js를 다음과 같이 수정하여 ES6에서 추가된 Promise, Object.assign, Array.from 등이 어떻게 트랜스파일링되는지 확인해보자.

//src/js/main.js

import { pi, power, Foo } from './lib';

// 폴리필이 필요한 코드
console.log(new Promise((resolve, reject) => {
  setTimeout(() => resolve(1), 100);
}));

console.log(Object.assign({}, { x:1 }, { y:2 }));

console.log(Array.from([1,2,3], v => v + v ));

다시 트랜스파일링과 번들링을 실행한 다음, dist/js/bundle.js를 확인해보면 ES5 사양으로 대체할 수 없는 기능은 트랜스파일링이 되지 않는 것을 볼 수 있다. 그렇기 때문에 @babel/polyfiil을 설치해야 한다.

 

npm install @babel/polyfill

@babel/polyfill은 개발 환경에서만 사용하는 것이 아니라 실제 운영 환경에서도 사용해야 한다. 따라서 개발용 의존성으로 설치하는 --save-dev 옵션을 지정하지 않는다.

ES6의 import를 사용하는 경우에는 진입점의 선두에서 먼저 폴리필을 로드하도록 한다.

 

// src/js/main.js

import '@babel/polyfill';
import { pi, power, Foo } from './lib'
...

Webpack을 사용하는 경우에는 위 방법 대신 webpack.config.js파일의 entry 배열에 폴리필을 추가한다.

 

const path = require('path');

module.exports = {
  //entry file
  entry: ['@babel/polyfill', '/src/js/main.js']
  ...
}

dist/js/bundle.js를 확인해보면 폴리필이 추가된것을 확인할 수 있다.