모던 자바스크립트 Deep Dive
49

Babel과 Webpack

Babel

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

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

Babel이 제공하는 공식 Babel 프리셋은 다음과 같다.

  • @babel/preset-env for compiling ES2015+ syntax
  • @babel/preset-typescript for TypeScript
  • @babel/preset-react for React
  • @babel/preset-flow for Flow

트랜스파일링

Babel CLI 명령어를 사용할 수도 있으나 매번 명령어를 입력하는 것은 번거로우므로 npm scripts에 Babel CLI 명령어를 등록하여 사용할 수 있다.

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

Babel이 모듈을 트랜스파일링한 결과는 Node.js가 지원하는 CommonJS 방식의 모듈 로딩 시스템을 따른다. 하지만 브라우저는 CommonJS 방식의 require 함수를 지원하지 않으므로 트랜스파일링된 결과를 그대로 브라우저에 실행하면 에러가 발생한다. 그래서 브라우저 ES6 모듈(ESM)을 사용하도록 Babel을 설정할 수 있으나, ESM이 아직 지원하지 않는 기능이 있고 해결중인 이슈가 있어 Webpack으로 이를 해결하는 것을 추천한다.

라고 책에 나와있는데 검색을 해보니 책이 출간된 21년 당시의 상황은 그랬지만 최신 상황을 고려하면 조금 얘기가 달라진다. 현재 대부분의 모던 브라우저(크롬, 파이어폭스, 사파리, 엣지 등)는 ES6 모듈을 기본적으로 지원하기 때문에 대부분의 경우에 문제가 되지 않는다. 하지만 번들러와 트랜스파일러는 여전히 중요한 역할을 한다. 특히 프로젝트의 복잡성이나 브라우저 호환성을 고려할 때는 Webpack과 Babel을 사용하는 것이 권장된다. 성능 최적화, 호환성, 개발 편의성 측면에서 유리하기 때문이다.

Webpack

의존 관계에 있는 자바스크립트, CSS, 이미지 등의 리소스들을 하나(또는 여러 개)의 파일로 번들링하는 모듈 번들러다. Webpack을 사용하면 의존 모듈이 하나의 파일로 번들링되므로 별도의 모듈 로더가 필요 없다. 그리고 여러 개의 자바스크립트 파일을 로드해야 하는 번거로움도 사라진다.

Webpack이 모듈을 번들링할 때 Babel을 사용하여 소스코드를 ES5 사양의 소스코드로 트랜스파일링하도록 babel-loader를 설치하고 설정하여 Webpack이 Babel을 실행하도록 해준다.

...
module: {
  rules: [
    {
      test: /\.(?:js|mjs|cjs)$/,
      exclude: /node_modules/,
      use: {
        loader: 'babel-loader',
        options: {
          presets: [['@babel/preset-env', { targets: 'defaults' }]],
        },
      },
    },
  ];
}
...

babel-polyfill

트랜스파일링해도 브라우저가 지원하지 않는 코드가 남아있을 수 있다. 예를 들어 ES6에서 추가된 Promise, Object.assign, Array.from 등은 ES5으로 대체할 기능이 없기 떄문에 트랜스파일링되지 못하고 그대로 남는다. 이런 객체나 메서드를 사용하기 위해서 @babel/polyfill을 설치해 주어야 한다.

@babel/polyfill은 실제 운영 환경에서도 사용해야하기 때문에 개발용 의존성으로 설치하지 않는다.