바벨은 ES6로 작성된 코드를 모든 브라우저에서 동작되도록 하위 버전으로 변환(트랜스파일)해주는 역할
을 한다. (트랜스파일러라고 함)
$ npm install -D @babel/core @babel/cli
바벨은 빌드 작업을 처리하기에 적합한 자료구조인 추상 구문 트리(AST
)로 변환하는 파싱단계
를 거치고 이 추상 구문 트리를 변경하는 변환단계
를 거치며 마지막으로 변경된 결과물을 출력하는 출력단계
로 진행된다.
(위에서 설치한 @babel/core는 파싱단계와 출력단계만)
하지만 바벨을 실행해보면 나오게 되는 출력 결과는 전과 동일한데
이는 아직 바벨에게 어떻게 변환해주어야 하는지 규칙을 알려주지 않았기 때문이다.
위의 결과에서는 변환단계에서 아무런 동작을 하지 않았다. 그래서 바벨에게 플러그인(plugin)
이나 프리셋(preset)
을 이용하여 어떻게 변환할지 규칙을 알려주면 된다.
플러그인
: 변환 규칙 하나하나를 의미
e.g. block-scoping 플러그인: const, let 처럼 블록 스코핑을 따르는 예약어를 함수 스코핑을 사용하는 var 변경,
arrow-functions 플러그인: 화살표함수를 일반함수로 변경
프리셋
: 목적에 맞게 여러 플러그인 규칙들을 모아놓은세트
직접 플러그인을 만들어서 플러그인의 동작원리를 살펴보자.
// 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("")
},
},
}
}
visitor객체 : 추상 구문 트리(AST) 접근 할 수 있는 메소드를 제공
Identifier() 메소드를 통해 AST의 노드에 접근 할 수 있다.
위의 app.js에서 작성한 코드를 플러그인을 사용하여 변환하기 위해 아래 세가지의 플러그인을 설치했다.
block-scoping: 블록 스코핑 예약어를 함수 스코핑 예약어로
arrow-functions: 화살표 함수를 일반함수로
strict-mode: "use strict" 구문을 추가
$ npm install -D @babel/plugin-transform-block-scoping @babel/plugin-transform-arrow-functions @babel/plugin-transform-strict-mode
커스텀 플러그인에서 사용한 커맨드라인 명령어도 가능하지만 플러그인이 많아질수록 코드가 길어지기 때문에 웹팩의 설정파일처럼 바벨도 설정파일을 제공한다.
// babel.config.js:
module.exports = {
plugins: [
"@babel/plugin-transform-block-scoping",
"@babel/plugin-transform-arrow-functions",
"@babel/plugin-transform-strict-mode",
],
}
플러그인에 의해 var, 일반함수, 엄격모드로 변경된것을 확인 할 수 있다.
위에서 사용된 세 가지의 플러그인을 하나의 프리셋으로 만들려면 아래와 같이 작성하면 된다.
// 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"],
}
위의 방식처럼 커스텀으로 프리셋을 설정할 수 있고 바벨에서도 목적에 따라 프리셋을 제공해주기 때문에 적절한 프리셋을 사용하면 된다.
지금까지는 동작원리를 이해하기 위해 알아봤지만 실제 실무에서는 아래와 같이 바벨에서 제공되는 프리셋을 사용한다고 한다.
preset-env 프리셋을 예시로 한다면,
preset-env
: ES6로 작성된 코드를 하위버전으로 변환할 때 사용$ npm i -D @babel/preset-env
// babel.config.js:
module.exports = {
presets: ['@babel/preset-env'],
}
만약 IE는 생각하지 않고 크롬 브라우저의 특정 버전까지 지원할 생각이라면 target옵션을 주어 불필요한 변환 작업을 하지 않도록 할 수 있다.
// babel.config.js:
module.exports = {
presets: [
['@babel/preset-env', {
targets: {
chrome: '95'
}
}]
]
}
위의 코드는 작성일자 기준의 최신 크롬 브라우저까지 지원하겠다는 의미이다.
따라서 불필요한 const, 화살표함수가 변환되지 않는 것을 확인 할 수 있었다.
위의 방식 말고도 점유율에 따른 타겟 브라우저 지정도 가능하다.
("시장점유율이 0.25%이상, 업데이트가 종료되지 않은 브라우저를 대상")"presets": [ ["@babel/preset-env", { "targets": "> 0.25%, not dead" }], ]
바벨은 모든 브라우저에서 코드가 돌아가게끔 변환해주는 작업을 해주지만 ES6이상에서 새롭게 추가된 Promise, map과 같은 객체들은 코드 변환이 어렵기 때문에 폴리필
이 필요하다.
예를들어 Promise 객체
는 ES6 에서 추가된 객체로, ES6 이전에서는 존재하지 않아 변환하지 못하기 때문에 폴리필을 사용하여 해결
해야 한다.
// babel.config.js:
module.exports = {
presets: [
[
"@babel/preset-env",
{
useBuiltIns: "usage", // 폴리필 사용 방식 지정
corejs: {
// 폴리필 버전 지정
version: 3,
},
},
],
],
}
When either the usage or entry options are used, @babel/preset-env will add direct references to core-js modules as bare imports (or requires). This means core-js will be resolved relative to the file itself and needs to be accessible.
useBuiltIns 옵션은 폴리필 삽입 방식을 설정하는 옵션으로 usage 또는 entry을 사용하면 core-js
모듈을 import 한다고 한다.
위의 코드에서는 폴리필 라이브러리 중 core-js@3를 사용
기존의 폴리필 라이브러리
@babel/polyfill
필요하지 않은 폴리필까지 번들에 포함되어있어 번들 크기가 커지는 단점이 존재하며 현재는 deprecated되었다고 함.
바벨을 따로 직접사용하는 것보다는 웹팩으로 통합해서 사용하는 것이 일반적이다.
$ npm install -D babel-loader
// webpack.config.js:
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader", // 바벨 로더를 추가한다
},
],
},
}
기존에 사용했던 웹팩 설정파일의 rules 배열 내에 새롭게 추가하면 되는데 여기서 exclude를 사용하여 node_modueles의 js파일이 번들되는 것을 방지한다.
$ npm i -D core-js@3
아까 위에서 폴리필 버전을 지정해둔 core-js@3 라이브러리
를 설치해야 오류가 발생하지 않는다.
$ npm run build
이제 npm run build를 실행하면 js파일에 대한 바벨 로더가 실행되고 바벨 로더는 바벨 설정파일을 찾아 설정한 방식대로 코드를 변환해준다.