본문 바로가기

Bundler

[Webpack] Babel 설정하기 ( compatibility , polyfill, env etc..)

 

Intro.

프로그래밍 하면서 하위 호환성(Backward Compatibility)라는 단어를 들어 보셨나요? 쉽게 말해, 특정 피쳐가 하위 호환을 지원한다는 뜻은 곧, 하위 버전에서 해당 피쳐를 사용할 수 있다는 뜻입니다.

 

JS로 프로그래밍 할 때도 해당 개념을 신경 써야합니다. 왜냐하면, 스크립트를 실행하는 브라우저가 굉장히 다양하고 또 어떤 브라우저(IE)는 더 이상 업데이트를 안하기 때문이지요.

 

그래서 최신 피쳐를 사용하려 하니 어떤 브라우저에선 되는데, 어떤 브라우저에선 에러를 던지는 현상을 간간이 보실 수 있습니다.

 

그리고 이러한 문제는 트랜스파일러(Transpiler)를 이용하여, 최신 코드를 특정 브라우저가 이해할 수 있는 코드로 바꾸는 방법이 있습니다.

 

이번 글에선, babel이란 트랜스파일러를 이용하여 해당 문제를 해결해보겠습니다.

 

Goal

Before -> After

 

 

 

Method

 

1. babel 관련 모듈을 설치합니다.

yarn add -D @babel/core @babel/preset-env babel-loader

 

2. babel 설정파일을 만듭니다.

 

babel이 트랜스파일링을 결정하는 방법은 해당 프로젝트가 어떤 브라우저를 지원하느냐에 따라 달립니다. 

 

따라서 먼저 .browserslistrc 를 만들어 해당 프로젝트가 어떤 브라우저를 지원할지 명시해줍니다.

 

저 같은 경우, 전체 시장 중 1%이상의 점유율을 갖는 브라우저 및 버전을 지원하겠습니다.

 

(** 중요 **) 무턱대고 모든 브라우저를 지원하겠다는 마음에 0% 로 설정하면 그만큼 번들 사이즈가 커집니다. 따라서 지원하고자 하는 타겟을 명확히 지정하고 진행합시다.

 

//.browserlistsrc

> 1%

 

이제 .bablerc 파일을 만들어 트랜스파일링 설정을 진행합니다.

 

//.babelrc

{
	"presets": [
        [
          "@babel/preset-env"
        ]
      ]
}

 

3. webpack이 babel을 핸들링할 수 있도록 설정을 추가합니다.

 

결국 babel의 역할은 모든 js파일을 브라우저가 지원하도록 트랜스파일링 하는 것이기에, 모든 js파일에 대해 babel-loader를 적용합니다.

 

단, 모든 js파일들에 대해 진행하기 때문에, node_modules도 기본적으로 포함됩니다. 따라서 exclude 프로퍼티를 추가하여 node_modules는 트랜스파일링 대상에서 제외합시다.

 

//webpack.common.config.js


module.exports = {
	..., // input output plugins etc...
    module: {
    rules: [
      ..., // other rules etc..
      {
        test: /\.js$/,
        exclude: /node_moduels/,
        use: ['babel-loader']
      }
    ]
  },
}

 

4. Polyfill(폴리필) 진행하기

 

대표적으로 Promise api의 경우가 ES6에서 추가된 피쳐이지요, 이 경우, ES5 이하 버전에선 지원이 안되기에, 이전 버전에서, 해당 기능을 쓸 수 있게 해주는 요소들을 추가해줘야합니다. 일종의 충전재(polyfill)이지요.

 

babel 7.4버전 이후부터 사용 가능합니다. @babel/preset-env의 "useBuiltIns" 옵션을 추가해, 지원안하는 피쳐들에 대해 자동으로 polyfill 가능합니다. 

 

해당 방식은 corejs 패키지에 의존하므로 해당 패키지 설치 후 진행합니다.

 

corejs 버전의 경우, 설치 되어 있는 모듈 버전을 명시하면 됩니다. 공식 문서상으론 3.0 이상을 권장합니다.

 

debug 프로퍼티 설정을 통해 어떤 피쳐가 폴리필 되었는지 확인 가능합니다

 

yarn add -D corejs
//.babelrc

{
	"presets": [
        [
          "@babel/preset-env",
          {
            "useBuiltIns": "usage",
            "corejs": {
              "version": 3.8,
              "proposals": true
            },
            "debug": true
          }
        ]
      ]
}

 

5. babel에 환경 변수 넣기

 

당연하지만, 폴리필의 경우 개발 환경에서 사용하면 오히려 빌드 타임 퍼포먼스만 악화시키기에, 개발 환경에선 사용하지 않는것이 권장됩니다. 따라서, production / dev 환경에 따라 폴리필 실행 여부를 나누고 싶습니다.

 

babel에선 package.json파일에 환경 변수를 명시함으로써, babelrc에 환경 변수를 넘겨 줄 수 있습니다.

 

BABEL_ENV -> NODE_ENV -> "dev"

 

BABEL_ENV가 존재하지 않을 시, 실행 스크립트는 NODE_ENV의 유무를 찾게 되고 이것마저 없을 시, "dev" 즉, 개발 환경으로 인식합니다.

 

따라서, 다음과 같이 스크립트를 짜 줍시다.

 

 "scripts": {
    "build": "NODE_ENV=production webpack --config webpack/webpack.prod.config.js",
    "dev": "NODE_ENV=dev webpack serve --config webpack/webpack.dev.config.js --hot"
  },

 

다만, 위의 방식은 Linux & OSX 체제에서만 사용하는 환경 변수 셋팅 방식으로, Window의 경우 다음의 형식으로 셋팅하여야합니다.

set (환경변수)=(값)&&

 

이는 , cross-env 라는 모듈을 통해 손 쉽게 해결 가능합니다.

 

먼저  cross-env 모듈을 설치합니다.

 

yarn add -D cross-env

 

그리고 package.json 스크립트에 cross-env를 명시해주면 끝입니다.

 

 "scripts": {
    "build": "cross-env NODE_ENV=production webpack --config webpack/webpack.prod.config.js",
    "dev": "cross-env NODE_ENV=dev webpack serve --config webpack/webpack.dev.config.js --hot"
  },

 

이제 .babelrc파일에서 각 환경별 설정만 다시해주면 됩니다.

 

저는 production 환경에서만 폴리필을 원하므로, dev환경에선 폴리필을 빼주겠습니다.

 

{
  "env": {
    "production": {
      "presets": [
        [
          "@babel/preset-env",
          {
            "useBuiltIns": "usage",
            "corejs": {
              "version": 3.8,
              "proposals": true
            },
            "debug": true
          }
        ]
      ]
    },
    "dev": {
      "presets": [["@babel/preset-env"]]
    }
  }
}

 

 

6. 실험적 요소 추가하기

 

개발하다가 정말 실험적인 피쳐를 사용할 수 도 있을것 같습니다. 오픈소스에서  말 그대로 자바스크립트에 proposal 단계로 제시되어 있는 피쳐가 있겠군요. 이런 요소들은 당연하지만 공식적으로 제공되는 기능들이 아니기에, corejs를 통한 폴리필도 기대할 수 없습니다.

 

따라서 직접 해당 플러그인을 babel 설정에 주입시켜야합니다.

 

예시로 pipeline-operator을 들어보겠습니다.

 

https://github.com/tc39/proposal-pipeline-operator

 

GitHub - tc39/proposal-pipeline-operator: A proposal for adding a useful pipe operator to JavaScript.

A proposal for adding a useful pipe operator to JavaScript. - GitHub - tc39/proposal-pipeline-operator: A proposal for adding a useful pipe operator to JavaScript.

github.com

 

말 그대로 해당 피쳐용 플러그인 설치 후, babel 설정 객체의 플러그인 프로퍼티에 추가만 하면 됩니다.

 

먼저 플러그인을 설치합니다.

 

yarn add -D @babel/plugin-proposal-pipeline-operator

 

그리고 plugins 프로퍼티에 추가합니다.

 

당연하지만, 개발 / 배포 환경과 관련없이 공통적으로 적용되어야하기에,  탑레벨에다 선언해놓겠습니다. 

만약 특정 환경에다만 적용하고 싶다면, 해당 환경 내에 plugins에 적용하면 됩니다.

 

{
  "plugins": [
    [
      "@babel/plugin-proposal-pipeline-operator",
      {
        "proposal": "minimal"
      }
    ]
  ],
  "env": {
    "production": {
      "presets": [
        [
          "@babel/preset-env",
          {
            "useBuiltIns": "usage",
            "corejs": {
              "version": 3.8,
              "proposals": true
            },
            "debug": true 
          }
        ]
      ]
    },
    "dev": {
      "presets": [["@babel/preset-env"]]
    }
  }
}