Babel preset-env 이해하기

Babel은 최신 JavaScript 문법을 구버전 브라우저에서도 동작하도록 변환해주는 트랜스파일러다. @babel/preset-env는 그중에서도 가장 많이 쓰는 프리셋이다.

preset이 뭔가

Babel은 플러그인 기반으로 동작한다. 화살표 함수를 변환하려면 @babel/plugin-transform-arrow-functions, 클래스를 변환하려면 @babel/plugin-transform-classes... 이런 식으로 기능마다 플러그인이 따로 있다.

근데 이걸 하나하나 설치하고 설정하기 귀찮으니까, 자주 쓰는 플러그인들을 묶어놓은 게 preset이다.

preset-env가 하는 일

@babel/preset-env는 타겟 브라우저에 맞춰서 필요한 변환만 해준다.

예를 들어 Chrome 최신 버전만 지원하면 화살표 함수는 그대로 두고, IE11까지 지원해야 하면 일반 함수로 변환한다. 불필요한 변환을 안 하니까 번들 크기도 줄고 성능도 좋아진다.

설치

npm install @babel/core @babel/preset-env -D

기본 설정

babel.config.js 또는 .babelrc에 설정한다.

// babel.config.js
module.exports = {
  presets: ['@babel/preset-env']
};

이대로 쓰면 기본값으로 동작하는데, 타겟을 지정하지 않으면 모든 ES6+ 문법을 ES5로 변환한다. 보통은 타겟을 지정해서 쓴다.

targets 옵션

지원할 브라우저를 지정한다.

module.exports = {
  presets: [
    ['@babel/preset-env', {
      targets: {
        chrome: '80',
        firefox: '78',
        safari: '14',
        edge: '80'
      }
    }]
  ]
};

browserslist 쿼리를 쓸 수도 있다.

targets: '> 1%, last 2 versions, not dead'

또는 package.json에 browserslist 필드를 추가하면 preset-env가 자동으로 읽는다.

{
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not dead"
  ]
}

useBuiltIns 옵션

Promise, Array.includes 같은 새로운 API는 문법 변환만으로 안 된다. 폴리필이 필요하다.

useBuiltIns 옵션으로 폴리필을 어떻게 처리할지 정한다.

false (기본값)

폴리필을 자동으로 추가하지 않는다. 직접 import해야 한다.

entry

엔트리 파일에서 core-js를 import하면, 타겟 브라우저에 필요한 폴리필만 import문으로 바꿔준다.

// 변환 전
import 'core-js/stable';

// 변환 후 (타겟에 따라 필요한 것만)
import 'core-js/modules/es.promise';
import 'core-js/modules/es.array.includes';
// ...

설정:

module.exports = {
  presets: [
    ['@babel/preset-env', {
      useBuiltIns: 'entry',
      corejs: 3
    }]
  ]
};

usage

코드에서 실제로 사용하는 기능만 폴리필을 추가한다. import문 없이도 자동으로 넣어준다.

// 소스 코드
const arr = [1, 2, 3];
arr.includes(2);

// 변환 후
import 'core-js/modules/es.array.includes';
const arr = [1, 2, 3];
arr.includes(2);

설정:

module.exports = {
  presets: [
    ['@babel/preset-env', {
      useBuiltIns: 'usage',
      corejs: 3
    }]
  ]
};

usage가 번들 크기 최적화에 유리하다. 대신 서드파티 라이브러리가 폴리필을 필요로 하는데 감지를 못하는 경우가 있어서, 상황에 따라 entry를 쓰기도 한다.

modules 옵션

모듈 시스템을 어떻게 변환할지 정한다.

module.exports = {
  presets: [
    ['@babel/preset-env', {
      modules: false
    }]
  ]
};
  • 'auto' (기본값): 자동 감지
  • false: ES 모듈 유지
  • 'commonjs': CommonJS로 변환

Webpack이나 Rollup 같은 번들러를 쓴다면 false로 설정하는 게 좋다. ES 모듈을 유지해야 트리 쉐이킹이 제대로 동작한다.

실제 설정 예시

// babel.config.js
module.exports = {
  presets: [
    ['@babel/preset-env', {
      targets: '> 1%, last 2 versions, not dead',
      useBuiltIns: 'usage',
      corejs: 3,
      modules: false
    }]
  ]
};

Webpack과 함께 쓸 때:

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: 'babel-loader'
      }
    ]
  }
};

정리

  • targets: 지원할 브라우저 범위 지정
  • useBuiltIns: 폴리필 처리 방식 (entry 또는 usage)
  • modules: false: 번들러와 함께 쓸 때 트리 쉐이킹을 위해 설정

타겟 브라우저에 맞춰서 최소한의 변환만 하기 때문에, 무조건 ES5로 내리는 것보다 번들 크기가 작아진다.