본문으로 바로가기
mark-lab

바벨

1. 배경

1.1 크로스 브라우징

브라우져마다 사용하는 언어가 달라서 프론트엔트 코드는 일관적이지 못할 때가 많다. 크로스브라우징의 혼란을 해결해 줄 수 있는 것이 바벨이다. ECMAScript2015+로 작성한 코드를 모든 브라우져에서 동작하도록 호환성을 지켜준다. 타입스크립트, JSX처럼 다른 언어로 분류되는 것도 포함한다.

크로스브라우징을 해결하기 위해 바벨 을 사용한다.

1.2 트랜스파일과 빌드

변환하는 것을 "트랜스파일" 한다라고 표현한다. 변환 전후의 추상화 수준이 다른 빌드와는 달리 트랜스파일은 추상화 수준을 유지한 상태로 코드를 변환한다. 타입스크립트 -> 자바스크립트, JSX -> 자바스크립트처럼 트랜스파일 후에도 여전히 코드를 읽을 수 있다.

2. 바벨의 기본 동작

바벨은 ECMAScript2015 이상의 코드를 적당한 하위 버전으로 바꾸는 것이 주된 역할이다. 이렇게 바뀐 코드는 인터넷 익스플로러나 구버전 브라우져처럼 최신 자바스크립트 코드를 이해하지 못하는 환경에서도 잘 동작한다.

const alert = (msg) => window.alert(msg)
npm install -D @babel/core  @babel/cli
 
npx babel app.js

node_modules에서 babel을 찾아 CLI를 실행해야 했으나 npx를 제공해서 node_modules에 babel을 찾아 실행하고 없으로 글로벌에서 찾아서 실행한다.

바벨은 세 단계로 빌드를 진행한다.

  • 파싱(Parsing)
  • 변환(Transforming)
  • 출력(Printing) 코드를 읽고 추상 구문 트리(AST)로 변환하는 단계를 파싱이라고 한다. 이것은 빌드 작업을 처리하기에 적합한 자료구조인데 컴파일러 이론에 사용되는 개념이다. 추상 구문 트리를 변경하는 것이 변환 단계이다. 실제로 코드를 변경하는 작업을 한다. 변경된 결과물을 "출력"하는 것을 마지막으로 바벨은 작업을 완료한다.

그런데 결과를 보면 빌드 이전과 변한게 없다.

3. 플러그인

바벨은 파싱과 출력만 담당하고 변환 작업은 플러그인 이라고 부른다.

3.1 커스텀 플러그인

// myplugin.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('')
      },
      VariableDeclaration(path) {
        console.log('VariableDeclaration() kind:', path.node.kind) // const
 
        if (path.node.kind === 'const') {
          path.node.kind = 'var'
        }
      },
    },
  }
}

플러그인 옵션으로 실행한다.

npx babel ./app.js --plugins ./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);

플러그인들을 모아둔 것을 프리셋이라고 한다.

4. 프리셋

4.1 커스텀 프리셋

module.exports = function mypreset() {
  return {
    plugins: [
      '@babel/plugin-transform-arrow-functions',
      '@babel/plugin-transform-block-scoping',
      '@babel/plugin-transform-strict-mode',
    ],
  }
}

플러그인과 프리셋이 있다. 변환하는것을 플러그인이라고 하고 이것들이 많다보니 모아둔것을 프리셋이라고 한다.

preset-env는 ECMAScript2015+를 변환할 때 사용한다.

5. env 프리셋 설정과 폴리필

5.1 타겟 브라우저 지정도 가능하다.

// babel.config.js
module.exports = {
  presets: [
    [
      '@babel/preset-env',
      {
        targets: {
          chrome: '79', // 크롬 79까지 지원하는 코드를 만든다
        },
      },
    ],
  ],
}

5.2 폴리필

ECMASCript2015의 Promise 객체를 사용하는 코드다.

new Promise()

useBuiltIns는 어떤 방식으로 폴리필을 사용할지 설정하는 옵션이다. "usage" , "entry", false 세 가지 값을 사용하는데 기본값이 false 이므로 폴리필이 동작하지 않았던 것이다. 반면 usage나 entry를 설정하면 폴리필 패키지 중 core-js를 모듈로 가져온다(이전에 사용하던 babel/polyfile은 바벨 7.4.0부터 사용하지 않음).

corejs 모듈의 버전도 명시하는데 기본값은 2다. 버전 3과 차이는 확실히 잘 모르겠다. 이럴 땐 그냥 기본값을 사용하는 편이다.

자세한 폴리필 옵션은 바벨 문서의 useBuiltIns와 corejs 섹션을 참고하자.

6. 웹팩으로 통합

실무 환경에서는 바벨을 직접 사용하는 것보다는 웹팩으로 통합해서 사용하는 것이 일반적이다

// webpack.config.js:
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader', // 바벨 로더를 추가한다
      },
    ],
  },
}

정리

  • 바벨은 일반적인 방식으로 코딩하면서, 다양한 브라우져에서 돌아가는 어플리케이션을 만들기 위한 도구이다.
  • 바벨의 코어는 파싱과 출력만 담당하고 변환 작언은 플러그인이 처리한다.
  • 여러개의 플러그인을 모아놓은 세트를 프리셋이라고 한다.
  • 바벨이 변환하지 못하는 코드는 폴리필이라 부르는 코드조각을 불러와 결과물에 로딩해서 해결한다.
  • babel-loader로 웹팩과 함께 사용해 자동화된 프론트엔드 개발환경을 갖출 수 있다.