[JS] Babel 이란?

February 29, 2020

webpack

Babel은 최신의 js코드를 normal한 예전의 js코드로 변환해준다. 많은 모던 웹 브라우저들이 ES6 문법을 어느정도 지원 하지만 부족한 상황이다. 따라서 바벨을 통해 크로스 브라우징(Cross Browsing)이슈를 해결할 수 있다.

// Babel Input: ES2015 arrow function
[1, 2, 3].map(n => n + 1)

// Babel Output: ES5 equivalent
[1, 2, 3].map(function(n) {
  return n + 1
})


📌 Contents

  1. Intro
  2. Plugins
  3. Presets
  4. Polyfill
  5. Webpack과 사용하기


1. Intro


패키지 설치

npm install --save-dev @babel/core @babel/cli 

프로젝트의 루트에 추가한 config 파일예시

//babel.confing.json
{
  "presets": [
    [
      "@babel/env",
      {
        "targets": {
          "chrome": "79",  // 예시) 크롬 79까지 지원하는 코드를 만든다.
          "edge": "17",
          "firefox": "60",
          "safari": "11.1",
        },
        "useBuiltIns": "usage",
      }
    ]
  ]
}

CLI 사용

./node_modules/.bin/babel src --out-dir lib

src디렉토리의 모든 JavaScript 파일을 분석하고 변환 한 내용을 적용한 다음 각 파일을 lib디렉토리에 출력한다.

./node_modules/.bin/babel 대신 npx babel 명령어를 사용할 수 있다. ( npx babel --help 명령어로 사용법을 볼 수 있다.)

2. Plugins


플러그인은 코드를 변환하게하는 작은 js프로그램이다. 바벨은 파싱과 출력을 담당하고 플러그인이 변환 작업을 처리한다. 자신만의 플러그인을 작성하여 변환을 코드에 적용할 수도 있고, 공식 플러그인을 사용할 수도 있다.

커스텀 플러그인 예시

// app.js
const fn = something => window.alert(something);
// babelPlugin.js
module.exports = function myplugin() {
  return {
    visitor: {
      Identifier(path) {
        const name = path.node.name;

        // 바벨이 만든 AST 노드를 출력
        console.log('Identifier() name:', name) 
        
        // 코드 문자열을 역순으로 변환
        path.node.name = name
          .split("")
          .reverse()
          .join("");
      }
    },
  };
}

--plugins 옵션에 플러그인을 추가하여 사용한다.

npx babel app.js --plugins ./babelPlugin.js
Identifier() name: fn
Identifier() name: something
Identifier() name: window
Identifier() name: alert
Identifier() name: something
const nf = gnihtemos => wodniw.trela(gnihtemos);

공식 플러그인

block-scoping이나 arrow-functions와 같은 공식 플러그인을 사용할 수도 있다. 화살표함수 플러그인만 설치해보자.

npm install --save-dev @babel/plugin-transform-arrow-functions

플러그인을 설치하고 마찬가지로 --plugins옵션을 지정한뒤 플러그인 이름을 지정하면 된다.

npx babel app.js --plugins @babel/plugin-transform-arrow-functions

또는
./node_modules/.bin/babel src --out-dir lib --plugins=@babel/plugin-transform-arrow-functions
// 결과
const fn = function (something) {
  return window.alert(something);
};

화살표함수가 일반 함수로 변경된 것을 확인할 수 있다.

3. Presets


하나하나 필요한 것마다 플러그인을 설정하기는 번거롭기 때문에 일종의 “플러그인 세트”프리셋(preset)을 사용할 수도 있다.

프리셋도 마찬가지로 커스텀 프리셋을 만들 수 있다.

커스텀 프리셋 예시

// src/app.js
const fn = something => window.alert(something);
// babelPreset.js
module.exports = function mypreset() {
  return {
    plugins: [
      "@babel/plugin-transform-arrow-functions",
      "@babel/plugin-transform-block-scoping"
    ]
  };
};

커맨드라인 명령어가 길어지기 때문에 웹팩처럼 설정 파일로 분리한다.

//babel.confing.json
{
  "presets": ["./src/babelPreset.js"]
}

플러그인을 설정한 것과 똑같은 결과를 얻는다.

npx babel ./src/app.js     

// 결과
var fn = function (something) {
  return window.alert(something);
};

공식 프리셋

공식 프리셋은 몇가지가 제공된다.

  • @babel/preset-env
  • @babel/preset-flow
  • @babel/preset-react
  • @babel/preset-typescript

env프리셋을 사용해보자. 구성이 없으면 이 프리셋은 모던 자바스크립트를 지원하기위한 모든 plugin을 포함한다.

npm install --save-dev @babel/preset-env

설치후 설정파일에도 추가한다.

{
  "presets": ["@babel/preset-env"]
}
npx babel ./src/app.js     

빌드하면 같은 결과를 출력한다.

envtargets옵션으로 브라우저를 지정할 수 있다.

//babel.config.json
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "chrome": "79"
        }
      }
    ]
  ]
}
npx babel ./src/app.js 

// 결과
"use strict";

const fn = something => window.alert(something);

옵션으로 크롬버전을 지정하였고, 크롬은 const와 화살표함수를 인식하기 때문에 이번에는 실행결과 변환이 되지 않았다.


{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "chrome": "79",
          "ie": "11"    ...
npx babel ./src/app.js 

// 결과
"use strict";

var fn = function fn(something) {
  return window.alert(something);
};

크롬과 인터넷 익스플로러 11을 지원하게 설정하면 익스플로러도 포함해야하기 때문에 변환이 된다.

4. Polyfill


아래와 같이 코드를 추가하고 실행하면 첫줄만 바뀌고 그대로 출력된다.

const fn = something => window.alert(something);
console.log(Array.from("foo"));new Promise();
$ npx babel ./src/app.js 

"use strict";

var fn = function fn(something) {
  return window.alert(something);
};

console.log(Array.from("foo"));
new Promise();

Promise또는 Map, Array.from등 바벨이 변환하지 못하는 코드도 있다. Polyfill은 이러한 새로 추가된 전역 객체들을 사용가능하게 만들어준다.

npm install --save @babel/polyfill
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "chrome": "79",
          "ie": "11"
        },
        "useBuiltIns": "usage",        "corejs": {          "version": "2"        }      }
    ]
  ]
}

useBuiltIns 옵션은 usage, entry, false 값이 있고 기본값은 false이다. 이 옵션은 어떻게 폴리필을 처리할지 구성한다.

이제 아까와는 다른 결과를 볼 수 있다.

$ npx babel ./src/app.js 
"use strict";

require("core-js/modules/es6.promise");
require("core-js/modules/es6.object.to-string");
require("core-js/modules/es6.string.iterator");
require("core-js/modules/es6.array.from");

var fn = function fn(something) {
  return window.alert(something);
};

console.log(Array.from("foo"));
new Promise();

폴리필 사용을 설정하면 core-js도 설치해야한다. corejs옵션은 2, 3 버전이 있고 기본값은 2이다.

$ npm i core-js@2

5. Webpack과 사용하기


Babel LoaderBabelwebpack을 이용하여 JavaScript파일을 변환한다.

설치

npm install -D babel-loader

사용

//webpack.config.js
module: {
  rules: [
    {
      test: /\.m?js$/,
      exclude: /(node_modules|bower_components)/,
      use: {
        loader: 'babel-loader',
        options: {
          presets: ['@babel/preset-env']
        }
      }
    }
  ]
}

js 확장자로 끝나는 파일은 babel-loader 가 처리한다. 이제 웹팩으로 빌드하고 dist 폴더의 main.js 를 살펴보면 변경된 코드 등의 결과를 볼 수 있다.

바벨이 하는 역할은 웹팩과 엄밀히 다르다. 웹팩은 모듈을 번들링하고, 바벨은 코드 자체를 변경시켜준다는 차이가 있다.

References


Loading script...
© 2021 INKYO JEONG. All rights reserved.