성경 창세기전에 나오기를 당시 사람들이 바벨탑을 쌓아 그 꼭대기가 하늘에 닿게 하려고 했다. 그런데, 하나님은 사람들의 그 오만함에 분노했고 사람들이 협업하는데 꼭 필요한 소통수단인 언어를 제각각으로 만들어버렸다고 한다.
히브리어로 '바벨'은 '혼돈'을 뜻한다.
웹개발에서도 개발자마다 프론트엔드에서 사용하는 언어가 다르다. 주로 사용되는 건 JS지만, 최신 문법을 사용할 수도 있고 안할 수도 있으며, TS를 사용할 수도 있다. 그리고 브라우저가 해석할 수 있는 언어 또한 다르다. 스펙과 브라우저는 날이갈수록 개선되고 있지만, 한국에서 약간의 점유율을 가지고 있는 인터넷 익스플로러는 최신 문법을 여전히 지원하지 않는다. 작년까지만해도 사파리 최신 브라우저에서는 Promise.prototype.finally
메소드를 사용할 수 없었다.
보통 개발자들 사이에서 '여러 브라우저에서 작성한 코드가 정상적으로 돌아갈 수 있게 만드는 행위'를 '크로스 브라우징을 지원하게 만든다'고 한다. 바벨을 통해 크로스 브라우징을 지원하게 만들 수 있다. 바벨은 ECMAScript2015 이후 최신 문법으로 작성한 코드를 모든 브라우저에서 동작하도록 호환성을 지켜준다. 또한, 타입스크립트, JSX처럼 다른 언어로 분류되는 것도 포함된다.
크로스 브라우징은 처음 프론트엔드 개발에 접근하는 개발자에게 어려움을 준다.
바벨은 트랜스파일러(transpiler)
로 불린다. A라는 소스코드 파일을 읽어서 B라는 소스코드로 파일로 변환시키는 행위를 트랜스파일(transpile)
이라고 한다.
.java
파일에서 .class
파일로 변하는 것과 같이 변환 전 후의 수준이 다른 '빌드'의 개념과 달리, '트랜스파일'은 추상화 수준이 같은 코드로 변환한다.
트랜스파일(transpile)
이란,transcompiler
에 있는pile
을 쓴 것이다.file
이 아니다.
npm install @babel/core @babel/cli
@babel/core
: 핵심적인 동작이 담겨있는 바벨 코어가 있다.@babel/cli
: 커멘드라인 명령어를 지원하기 위한 바벨 CLI도 있다.const alert = (msg) => window.alert(msg);
먼저 최신 문법인 화살표 함수를 이용하여 테스트할 js파일을 하나 작성해두었다.
npx babel custom-babel-test.js
위는 babel
명령어를 통해 테스트용으로 작성했던 js를 트랜스파일해보자.
오잉? 결과가 트랜스파일 하기 전이랑 똑같다. 왜 그럴까? 먼저 바벨의 빌드 단계부터 알아보자.
파싱(Parsing)
: 코드를 읽고 추상 구문 트리(AST)로 변환하는 단계를 "파싱" 이라고 한다. 빌드 작업을 처리하기 적합한 자료구조인데, 컴파일러 이론에 사용되는 개념이다.변환(Transforming)
: 추상 구문 트리를 변경하는 것이 "변환" 단계이다. 실제로 코드를 변경하는 작업을 한다.출력(Printing)
: 변경된 결과물을 "출력"한다. 이 단계를 마지막으로 바벨이 작업을 완료한다.이전에 명령어를 입력했을 때, 결과가 바벨을 타기 전과 같았으니, 아마 변환 단계에서 아무런 일도 일어나지 않은 것 같다.
바벨은 기본적으로는 위에서도 설명했듯, 소스코드를 받아서 소스코드를 반환한다. 근데 그 과정에서 바벨은 파싱과 출력만 담당하게 되고 변환 작업은 "플러그인"이 처리하게 된다.
바벨의 코드 변환은 플러그인 (혹은 preset)을 설정 파일에 적용함으로써 활성화됩니다. from 바벨 공식문서
바벨 플러그인을 만드는 상세한 방법은 바벨 핸드북에 적혀있다.
custom-babel-plugin.js
파일 생성하기
module.exports = function customBabelPlugin() {
return {
// 보통 visitor라는 객체를 만들게 됨
visitor: {
Identifier(path) {
const name = path.node.name;
// 바벨이 만든 AST 노드 출력
console.log("Identifier() name:", name);
// 변환 작업: 코드 문자열을 역순으로 변환
path.node.name = name.split("").reverse().join("");
},
},
};
};
visitor
: 프로퍼티를 반환하는 함수로 시작된다.visitor
프로퍼티 내부에 Identifier
메소드를 구현한다.Identifier
가 받을 path
에서는 node.name
등 많은 정보를 가져올 수 있다.바벨은 AST (Abstract Syntax Tree)의 자바스크립트 버전인 ESTree를 사용한다. 바벨에서 사용하는 파서의 코어한 스펙은 전부 여기 공식 스펙 문서에서 확인해볼 수 있다.
npx babel --help
명령어를 쳐보면 babel
에 어떤 명령어들이 있는지 알 수 있다.
여기서 우리는 바벨의 plugin
을 사용해보는 것이니까 plugins
를 이용해 명령어를 입력해보자.
npx babel custom-babel-test.js --plugins './custom-babel-plugin.js'
실행 결과는 위와 같은데, 이전에 작성했던 js파일의 구문분석 결과로 AST 노드의 토큰 하나하나를 path.node.name
으로 가지고 있다.
그리고 각 토큰이 split("").reverse().join("")
에 의해 순서가 반대가 되어 있다.
module.exports = function customBabelPlugin() {
return {
// 보통 visitor라는 객체를 만들게 됨
visitor: {
VariableDeclaration(path) {
if (path.node.kind === "const") {
path.node.kind = "var";
}
},
},
};
};
위와 같이 작성해주면, 올드한 구형 브라우저에서 호환되지 않는 const
를 사용하는 변수를 찾아 var
로 변환해 줄 수 있다.
이전에 작성해본 customBabelPlugin
은 사실 바벨에서 플러그인 형태로 이미 제공해주고 있다.
바벨 플러그인의 babel-plugin-transform-block-scoping 설명 페이지에 가보면 예제에서 let
을 var
로 바꿔주는 것을 확인할 수 있다.
npm install @babel/plugin-transform-block-scoping
npx babel custom-babel-test.js --plugins @babel/plugin-transform-block-scoping
--plugins
뒤에 설치했던 패키지명을 그대로 입력해주면 된다.
실행결과 const
가 var
로 바뀌어있는 것을 확인할 수 있다.
arrow-functions는 IE에서 인식하지 못하는 화살표 함수를 일반 자바스크립트 함수로 트랜스컴파일해줄 수 있는 기능을 가진 플러그인이다.
npx babel custom-babel-test.js --plugins @babel/plugin-transform-block-scoping,@babel/plugin-transform-arrow-functions
플러그인을 두개 이상 적용하고 싶다면
,
컴마로 이어주면 된다.
결과를 보면, 화살표 함수도 사라져있고 const
도 var
로 바뀐 것을 확인할 수 있다.
strict-mode는 자바스크립트 문법에 엄격 모드를 적용시키는 플러그인이다.
npm install @babel/plugin-transform-strict-mode
"use strict"
가 추가되었다.
babel.config.js
파일을 프로젝트 메인에 생성해보자.
module.exports = {
plugins: [
"@babel/plugin-transform-block-scoping",
"@babel/plugin-transform-arrow-functions",
"@babel/plugin-transform-strict-mode",
],
};
이전처럼 플러그인 옵션을 안주고 그냥 npx babel custom-babel-test.js
를 해도 결과가 플러그인을 적용한 것처럼 잘 나온다.
실무 프로젝트에서는 바벨의 수많은 플러그인을 사용해야 하는데, 바벨의 플러그인을 하나하나 설치하다가는 시간 소요가 엄청날 것이며, 그루핑하여 관리하기도 쉽지 않을 것이다. 목적에 맞게 여러 개의 플러그인을 모아놓은 것을 "프리셋(preset)"이라고 부른다.
관계로 표현하자면 n개의 plugin
이 1개의 preset
이 된다.
custom-babel-preset.js
파일에 아래와 같은 내용을 넣어주었다.
module.exports = function customBabelPreset() {
return {
plugins: [
"@babel/plugin-transform-block-scoping",
"@babel/plugin-transform-arrow-functions",
"@babel/plugin-transform-strict-mode",
],
};
};
babel.config.js
의 내용을 다음과 같이 변경했다.
module.exports = {
preset: ["./custom-babel-preset.js"],
// plugins: [
// "@babel/plugin-transform-block-scoping",
// "@babel/plugin-transform-arrow-functions",
// "@babel/plugin-transform-strict-mode",
// ],
};
플러그인들이 뭉쳐진 프리셋이 잘 적용됐다.
바벨에서는 목적에 따라 자주 사용되는 몇가지 프리셋이 있다.
preset-env
: ECMAScript2015+를 변환할 때 사용한다. 바벨7이전에는 babel-reset-es2015
와 같이 버전별로 제공되었지만, 현재는 통합되어 사용하기 편리해졌다.preset-flow
: flow를 변환한다.preset-react
: react를 변환한다.preset-typescript
: 타입스크립트를 변환한다.인터넷 익스플로러 지원을 위해 env 프리셋을 사용해보자.
npm install -D @babel/preset-env
module.exports = {
presets: ["@babel/preset-env"],
};
실무에서는 이렇게 프리셋을 갖다쓴다.
babel.config.js
작성하기
module.exports = {
presets: [
[
"@babel/preset-env",
{
targets: {
chrome: "79",
},
},
],
],
};
아까와 다르게 presets
내부 @babel/preset-env
뒤에 targets
가 추가되었다.
이번에는 const
와 arrow-function
모두 그대로 유지됐다. 둘 다 chrome
에서 사용하기에 문제 없는 문법이기 때문이다.
babel-preset-env의 공식문서에 가보면 사용할 수 있는 다양한 옵션들이 나타나있다.
chrome
외에도 opera
, firefox
, safari
, ie
, ios
, android
, node
, electron
등이 있다.
CAN I USE 닷컴에서 내가 어떤 문법을 어떤 브라우저에서 사용할 수 있는지 볼 수 있다.
module.exports = {
presets: [
[
"@babel/preset-env",
{
targets: {
chrome: "79",
ie: "11",
},
},
],
],
};
이렇게 두 개 이상의 브라우저를 설정할 수도 있다.
ie
에 잘 호환되도록 코드가 바뀌었다.
app.js
라는 파일을 만들어서 다음과 같은 내용을 추가했다.
new Promise();
Promise
는 es6에 있는 객체이다.
ie
에서는 분명 Primise
를 지원하지 않는데, new Promise()
가 그대로 들어있다. 바벨은 ESCMAScript2015+에서 ECMAScript5 버전으로 변환할 수 있는 것들만 빌드한다. 그렇지 못한 것들은 "폴리필"이라고 부르는 코드 조각들을 추가해서 해결해야 한다.
이전의 ECMAScript2015의 블록 스코핑은 ECMAScript5의 함수 스코핑으로 대체가 가능했고, 화살표 함수도 일반 함수로 대체가 가능했다. 그러나 Promise
는 ECMAScript5 버전으로 대체할 수 없다. 다만, ECMAScript5 버전으로 구현할 수는 있다.
babel-preset-env
는 옵션으로 폴리필을 지정할 수 있는 옵션을 제공한다. useBuiltIns
는 어떤 방식으로 폴리필을 사용할지 설정하는 옵션이다. "usage"
, "entry"
, false
세가지 값을 사용하는데, 기본 값이 false
이므로 폴리필이 동작하지 않았던 것이다. 반면 "usage"
혹은 "entry"
를 설정하면 폴리필 패키지 중 core-js를 모듈로 가져온다.
babel.config.js
에서 아래와 같이 설정하면 된다.
module.exports = {
presets: [
[
"@babel/preset-env",
{
targets: {
chrome: "79",
ie: "11",
},
useBuiltIns: "usage",
corejs: {
version: 3,
},
},
],
],
};
corejs를 사용하려면
npm install core-js@3
명령어로core-js
를 설치해야 한다.
잘읽었습니다 감사합니다!