logo
menu

🤔 바벨에 대하여 알아보자!

2022. 06. 10.

  • #개발환경

💡
김정환 개발자님의 블로그를 참고하여 학습했고 이를 기반으로 정리한 내용입니다.

알게될 내용

  • 바벨에 대하여 알 수 있다.
  • 바벨의 동작 과정을 알 수 있다.
  • 바벨의 플러그인에 대해 알 수 있다.
  • 바벨의 프리셋에 대해 알 수 있다.
  • 폴리필에 대하여 알 수 있다.

개요

실제 개발 환경은 웹팩과 바벨을 같이 사용해서 개념들이 정확하지 않은 경우 헷갈리는 경우 있었다. 그래서 바벨에 대해 정확히 학습하고자 했고 해당 내용을 공유하고자 한다.
 
바벨에 대하여 알기 전에 바벨을 설치하자
npm install -D @babel/core @babel/cli
  • @babel/core : 바벨 기본적인 기능
  • @babel/cli : 터미널 환경에서(command) 사용하기 위함

바벨이란?

등장 배경

사람들이 하늘을 올라가기 위해 바벨 탑을 쌓고자 했는데 이 것을 본 하나님이 사람들이 사용하는 언어를 다 바꿔서 서로 소통하지 못하게하였다. 그래서 바벨 탑 쌓는 것을 실패하게 됨.
 
이것과 유사하게 브라우저에서 사용하는 JS 언어가 조금씩 다르다. 그래서 각 브라우저에 맞게 처리해야하므로 코드가 일관적이지 못할 경우가 많다.
예를 들어 Safari 브라우저에서 Promise.prototype.finally 메소드를 인식하지 못하고 IE 브라우저는 화살표 함수를 인식하지 못한다.
이런 문제를 해결 할 수 있는 것이 바벨이다.
 

바벨 너는 누구니?

바벨은 ES6+ 으로 작성된 코드를 모든 브라우저에서 일관되게 동작하도록 코드 변환(트랜스파일링) 작업을통해 호환성을 지켜줌.
즉, ES6+(이상) 의 코드를 ES5-(이하) 코드로 변환작업을 해서 모든 브라우저에서 동작하도록 코드 변환 작업을 수행함.
또한, TypeScript, JSX 등 다른 언어로 분류되는 것도 포함.
 

바벨의 동작 과정

  • 바벨을 수행하는 과정을 빌드라고 함.
동작 과정을 알아보기 전에 간단한 테스트를 해보자 (ES6 문법을 ES5 로 변환하는 작업)
  • src/app.js
    • // src/app.js: const alert = msg => window.alert(msg)
      npx babel app.js # const alert = msg => window.alert(msg);
수행을 시켰는데 기존 코드와 달라지지 않았다.
 
바벨은 총 3 단계로 빌드를 수행한다.
  1. 파싱 (Parsing)
    1. 코드를 읽고 추상 구문 트리(AST) 로 변환하는 단계
      파싱 단계에서는 const 토큰, alert 토큰, = 토큰 등으로 하나 토큰으로 분해하는 과정
  1. 변환 (Transforming)
    1. 추상 구문 트리를 변경 (실제 코드 변경 단계)
      ES6 로 되어 있는 코드를 ES5 로 변환?
  1. 출력 (Printing)
    1. 변경된 결과물 출력
       
위 예에서 코드가 변경되지 않았는데 이로 인해서 파싱을 했지만, 변환하지 않음을 확인할 수 있다.
그렇다면 어떻게 변화할 수 있을까? 바벨의 플러그인을 이용하여 변환 단계를 진행할 수 있다.
 

플러그인

바벨은 플러그인을 통해 변환 작업을 수행한다.
 

커스텀 플러그인을 구현해 보면서 자세히 알아보자!

// myplugin.js: module.exports = function myplugin() { return { visitor: { Identifier(path) { const name = path.node.name // path.node.name 은 파싱된 결과물에 접근 가능 (토큰 값) // 바벨이 만든 AST 노드를 출력한다 console.log("Identifier() name:", name) // 변환작업: 코드 문자열을 역순으로 변환한다 path.node.name = name.split("").reverse().join("") }, }, } }
바벨을 수행하면
npx babel app.js --plugins ./myplugin.js
  • --plugins 옵션을 통해 플러그인을 추가해 바벨을 실행시킬 수 있다.
npx babel ./babel-app.js --plugins ./babel/my-babel-plugin.js # Identifier() name: alert # Identifier() name: msg # Identifier() name: window # Identifier() name: alert # Identifier() name: msg # const trela = gsm => wodniw.trela(gsm);
  • 변환 작업을 통해 path.node.name 값이 역순으로 출력된 것을 확인할 수 있다.
이렇게 플러그인을 통해 바벨 작업시 변환 작업을 수행할 수 있다.
추가적으로 const 키워드를 var 키워드로 변환하고 싶다면 아래와 같이 작성할 수 있다.
// myplugin.js: module.exports = function myplugin() { return { visitor: { // https://github.com/babel/babel/blob/master/packages/babel-plugin-transform-block-scoping/src/index.js#L26 VariableDeclaration(path) { console.log("VariableDeclaration() kind:", path.node.kind) // const if (path.node.kind === "const") { path.node.kind = "var" } }, }, } }
 

플러그인을 사용해 바벨 작업하기

위에서 커스텀 플러그인을 작성하고 수행하면서 바벨의 동작 과정을 알아보았다.
바벨에서 제공하는 플러그인을 통해 기존 코드를 변환 작업을 수행하자.
사용하는 바벨은 아래와 같다.
  • @babel/plugin-transform-block-scoping
    • block-scoping 플러그인으로 const, let 와 같은 블록 스코핑을 따르는 예약어를 var 로 변환한다.
  • @babel/plugin-transform-arrow-functions
    • arrow-functions 플러그인을 이용해서 화살표 함수를 일반 함수로 변경할 수 있다.
  • @babel/plugin-transform-strict-mode
    • strict-mode 플러그인은 use strict 구문을 추가한다.
 
설치 및 수행
npm install -D @babel/plugin-transform-block-scoping \ @babel/plugin-transform-arrow-functions \ @babel/plugin-transform-strict-mode
npx babel ./babel-app.js \ --plugins @babel/plugin-transform-block-scoping \ --plugins @babel/plugin-transform-arrow-functions \ --plugins @babel/plugin-transform-strict-mode # "use strict"; # # var alert = function (msg) { # return window.alert(msg); # };
  • 이렇게 변환된 것을 확인할 수 있다.
 
하지만 이렇게 스크립트로 수행할 때 --plugins 로 각각의 플러그인을 추가하는 것은 어려움이 존재한다. 그래서 바벨은 babel.config.js 파일로 바벨 설정 파일을 제공한다.
 
바벨 설정 파일 추가
// babel.config.js: module.exports = { plugins: [ "@babel/plugin-transform-block-scoping", "@babel/plugin-transform-arrow-functions", "@babel/plugin-transform-strict-mode", ], }
  • 바벨은 plugins 배열을 통해 플러그인을 추가할 수 있다.
npx babel app.js # "use strict"; # # var alert = function (msg) { # return window.alert(msg); # };
 

프리셋

위 예에서 살펴 보았듯이 코드 한 줄을 변환하는데 여러 플러그인이 필요했다. 코드를 작업할 때 마다 바벨 플러그인을 추가하는 것은 어려움이 존재한다.
그래서 목적에 맞게 여러 플러그인을 세트로 모아놓은 것을 프리셋이라 한다.
 

커스텀 프리셋

// mypreset.js module.exports = function mypreset() { return { plugins: [ "@babel/plugin-transform-arrow-functions", "@babel/plugin-transform-block-scoping", "@babel/plugin-transform-strict-mode", ], } }
// babel.config.js module.exports = { presets: ["./mypreset.js"], }
  • 바벨에서 프리셋을 사용한다면, presets 배열에 추가해야한다.
 

프리셋 사용하기

바벨은 목적에 따라 다양한 프리셋을 제공한다.
  • preset-env
    • ES6+ 이상을 변환할 때 사용
  • preset-react
    • 리액트를 변환하기 위한 프리셋
  • preset-typescript
    • 타입스크립트를 변환하기 위한 프리셋
 

env 프리셋 사용해보기!!

설치 및 사용하기
npm install -D @babel/preset-env
// babel.config.js: module.exports = { presets: ["@babel/preset-env"], }
npx babel app.js # "use strict"; # # var alert = function alert(msg) { # return window.alert(msg); # };
이렇게 프리셋을 통해 간단하게 바벨을 사용할 수 있다.
 
타겟 브라우저
env 프리셋은 타겟 브라우저를 설정해 변환시키고자 하는 브라우저에 맞게 최적의 코드로 변환한다.
예를 들어 최신 버전인 Chrome 97 은 const, 화살표 함수 모두 지원하기 때문에 변환 작업이 필요하지 않다. 하지만, IE 11 과 같이 const, 화살표 함수를 지원하지 않은 경우 변환 작업이 필요하다.
 
// babel.config.js module.exports = { presets: [ [ "@babel/preset-env", { targets: { // chrome: "79", ie: "11", // ie 11까지 지원하는 코드를 만든다 }, }, ], ], }
npx babel app.js # "use strict"; # # var alert = function alert(msg) { # return window.alert(msg); # };
이렇게 타겟을 설정하면 바벨은 환경에 맞춰 변환 작업을 수행한다.

폴리필

이번엔 변환과 조금 다른 플리필에 대해 알아보자.
폴리필을 알기 위해 기존의 코드에 ES6에서 사용하는 Promise 객체를 추가하자
// app.js const alert = (msg) => window.alert(msg); new Promise();
npx babel app.js # "use strict"; # ... (앞으로 기존의 const, arrow 함수는 생략하겠다.) # new Promise();
바벨을 수행시켰는데 Promise 가 변환되지 않은 것을 확인할 수 있다.
그렇다면 왜 Promise 는 변환되지 않은걸까??
왜냐하면 바벨은 ES6+ 작성된 코드를 ES5- 로 변환할 수 있는 것만 빌드한다.
그래서 ES6 의 블록 스코핑은 함수 스코핑으로 화살표 함수는 일반함수로 대체할 수 있다.
하지만, Promise 는 ES5- 으로 대체할 수 없다.(왜냐하면 Promise 는 ES6에 나온 개념) 그래서 ES5 버전으로 구현된 Promise 코드를 추가(폴리필) 하여 사용할 수 있다. (참고: core-js promise)
 
따라서 폴리필은 코드 조각이다.
 

env 프리셋에서 폴리필 사용하기

// babel.config.js: module.exports = { presets: [ [ "@babel/preset-env", { useBuiltIns: "usage", // 폴리필 사용 방식 지정 corejs: { // 폴리필 버전 지정 version: 2, }, }, ], ], }
  • useBuiltIns
    • 어떤 방식으로 폴리필을 사용할지 설정하는 옵션
      default 값은 false 이고, usage, entry 로 폴리필을 사용할 수 있다.
npx babel src/app.js # "use strict"; # # require("core-js/modules/es6.object.to-string.js"); # # require("core-js/modules/es6.promise.js"); # # ... # # new Promise();
여전히 new Promise 를 사용하고 있지만, 위에 require 로 promise 에서 사용할 수 있도록 core-js 패키지로부터 프라미스 모듈을 가져온 것을 확인할 수 있다.
 
여기까지 폴리필에 대하여 알아봤다.
 

웹팩과 통합

실제로 바벨을 사용할 때는 웹팩과 같이 사용하는 것이 일반적이다.
웹팩에서 바벨을 사용해보자 babel-loader
 
설치
npm install -D babel-loader
// webpack.config.js: module.exports = { module: { rules: [ { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader", // 바벨 로더를 추가한다 }, ], }, }
.js 확장자로 끝나는 파일은 babel-loader가 처리하도록 했다.
우리는 폴리필도 설정했기 때문에 core-js 도 설치해야한다.
왜냐하면 웹팩은 바벨 로더가 만든 아래 코드를 만나면 core-js 를 찾기 때문이다.
// require("core-js/modules/es6.object.to-string.js"); // // require("core-js/modules/es6.promise.js");
 
core-js 설치 및 웹팩 수행
npm i core-js@2
npx webpack
빌드된 결과를 확인하면 core-js 의 promise 가 로드될 것을 확인할 수 있다.
notion image
 

정리

  • 바벨탑이 서로의 언어가 달라져 실패했듯이, 같은 JS라도 브라우저마다 차이가 있는데 이런 차이점을 공통된 형태로 변환해주는 것이 바벨이라는 것을 알게되었다.
    • 이렇게 서로 다른 브라우저에서 사용할 수 있게 변환(트랜스컴파일링)하는 이유는 일관된 코드를 작성하기 위함이다.
  • 바벨의 빌드 과정은 총 3가지 형태(파싱, 변환, 출력)로 동작한다.
  • 바벨은 변환 작업할 때 플러그인을 통해 변환 작업을 수행한다.
  • 플러그인의 세트를 프리셋이라 한다. (env 등)
  • 폴리필이라는 개념도 알아봤다.
    • 바벨을 통해서 변환될 수 있는 작업은 변환이 되지만, ES5- 버전에 없는 개념 (Promise 등)은 변환되지 않기 때문에 폴리필이라는 코드 조각을 추가해서 변환되지 않은 코드도 사용할 수 있게 했다.
  • 마지막으로 바벨을 웹팩에서 사용하는 방법을 알아봤다.
 
웹팩과 바벨에 대하여 확실히 알 수 있는 계기가 되었으면 좋겠다.
 

reference