바벨
프론트엔드의 코드들은 각각의 브라우저 상황에 맡게 돌아가기 폴리필등의 이유 때문에 일관적이지가 못하다
진입장벽을 높히고 협업을 힘들게 하기도 하는데 이런 단점을 보완하기 위해 나온 것이 바벨.
바벨은 ECMAScript2015+로 작성한 코드를 모든 브라우저에서 일관적이게 동작하도록 호환성을 지켜준다
바벨의 동작
바벨은 3단계를 거쳐서 빌드한다
파싱
=> 코드들을 토큰으로 나누어서 파싱하고변환
=> 파싱한 코드들을 변환해서출력
=> 출력한다
마이크로소프트의 인터넷 익스플로러는 자바스크립트 ES6의 문법을 지원하지 않는다
그래서 const
나 let
arrow function을 사용할 수 없는데
바벨을 사용하면 동작할 수 있게 트랜스파일링해준다
우선 IE에서 돌아가지 않을만한 코드 하나를 작성해보자
// app.js
const alert = msg => window.alert('msg');
alert 함수는 메세지를 파라미터로 받아서 alert으로 띄워주는 함수다
터미널에 바벨을 실행해보자
sample % npx babel app.js // 바벨 실행
const alert = msg => window.alert('msg'); // 출력 결과물
왜 빌드 결과물이 전과 똑같을까?
플러그인
바벨은 사실 파싱하고 출력만을 담당하고
플러그인들이 변환을 담당한다 현재 적용된 플러그인이 하나도 없기 때문에 파싱되고 난 후에 아무것도 변환하지 않고 출력된 것
커스텀 플러그인
여태까지와 같이 플러그인을 만들어보면서 동작 원리를 이해해보자
module.exports = function myBabelPlugin(){
return{
visitor:{
Identifier(path){
const name = path.node.name;
console.log('Identifier() name',name);
}
}
}
}
바벨의 플러그인은 vistor 객체를 반환해야한다
path.node.name로 파싱된 결과물들에 접근할 수 있다
위의 플러그인은 파싱된 결과물들을 하나하나씩 콘솔로그에 띄우는 일만 하는 플러그인인데
cli로 바벨을 실행할 때 옵션을 주는 법은 babel '파일명' --plguins '플러그인 경로' 이다
sample % npx babel app.js --plugins './my-babel-plugin.js'
Identifier() name alert
Identifier() name msg
Identifier() name window
Identifier() name alert
const alert = msg => window.alert('msg');
파싱된 하나하나의 결과물에 접근해서 로그를 띄우는 것을 볼 수 있다
가령 아래와 같이 해주면 코드의 좌우를 뒤집는 플러그인이 만들어지게 되는 것
Identifier(path){
const name = path.node.name;
path.node.name = name.split('').reverse().join('');
위에서 Identifier를 사용할 때 이상한 것을 느꼈는데 바로 const가 파싱되어 들어오지 않았다는 것이다
VariableDeclaration라는 함수를 사용하면 const까지 파싱되어 접근할 수 있다
visitor:{
VariableDeclaration(path){
console.log('VariableDeclaration() kind', path.node.kind);
if(path.node.kind === 'const'){
path.node.kind = 'var';
}
}
}
const를 잡아와서 var로 바꿔주는 플러그인이 만들어졌다
sample % npx babel app.js --plugins './my-babel-plugin.js'
VariableDeclaration() kind const
var alert = msg => window.alert('msg');
진짜 플러그인
당연하게도 이러한 플러그인들은 수요가 많기 때문에 직접 작성해서 사용할 필요가 없다
바벨에 이미 존재하고 있는 플러그인들을 가져와서 사용해보자
@babel/plugin-transform-block-scoping : const를 바꿔주는 플러그인
@babel/plugin-transform-arrow-functions : arrow funtion을 일반 함수로 바꿔주는 플러그인
각각 설치한 후 플러그인을 적용해서 바벨을 돌려보자
sample % npx babel app.js \
> --plugins @babel/plugin-transform-block-scoping \
> --plugins @babel/plugin-transform-arrow-functions
var alert = function (msg) {
return window.alert('msg');
};
babel.config.js 설정하기
매번 이렇게 명령어로 길게길게 적어서 실행할 순 없을 것 같다
바벨도 웹팩처럼 설정 파일을 제공한다
// babel.config.js
module.exports = {
plugins: [
"@babel/plugin-transform-block-scoping",
"@babel/plugin-transform-arrow-functions",
"@babel/plugin-transform-strict-mode",
]
}
이렇게 설정해두면 이제 babel만 실행시켜도 플러그인이 다 적용된 상태로 빌드가 된다
preset
이런 플러그인들을 모아놓은 것을 프리셋이라고 하는데 용도에 맞는 플러그인들을 모아놓은 꾸러미..? 라고 생각하면 될 것 같다
그래서 실제로 사용할 때는 만들어진 프리셋을 가져다가 쓰는 경우가 많다
어떤식으로 작동하는지 보기위해 위의 3가지의 플러그인을 가지고 프리셋을 만들어보자
// my-babel-preset.js
module.exports = function myBabelPreset(){
return{
plugins: [
"@babel/plugin-transform-block-scoping",
"@babel/plugin-transform-arrow-functions",
"@babel/plugin-transform-strict-mode",
]
}
}
// babel.config.js
module.exports = {
presets: [
'./my-babel-preset.js'
]
}
실제 프리셋
커스텀 프리셋을 만들고 사용해봤으니
실제로 바벨에서 제공하는 프리셋을 사용해 보자
자주 사용하는 프리셋은
preset-env, preset-flow, preset-react, preset-typescript 정도가 있는데
flow, react, typescript는 이름에서 알 수 있듯이 각각의 언어를 위한, env는 ECMAScript2015+를 사용할 때 쓰는 프리셋이다
npm i @babel/preset-env
로 설치하고
babel.config.js에서 프리셋을 수정한 뒤 사용해주자
module.exports = {
presets: [
'@babel/preset-env',
]
}
타겟 브라우저
바벨의 역할은 다양한 브라우저에서 최신 자바스크립트가 작동하도록 변환시켜주는 것이라고 했다
하지만 프로젝트가 서비스할 브라우저가 크롬 뿐이라면?
IE가 지원하지 않고 크롬에서 지원하는 기능을 굳이 IE까지 호환되게끔 변환해줄 필요는 없다
그럴 때 사용하는 것이 babel의 타겟 브라우저 옵션이다
module.exports = {
presets: [
['@babel/preset-env',{
targets: {
chrome: '89'
}
}]
],
}
이제 다시 babel을 돌려서 app.js를 빌드해보자
const alert = msg => window.alert('msg');
크롬에서는 const와 arrow function을 전부 호환하기 때문에 변환할 필요가 없기 때문에 그대로 출력하고 있다
targets ie : 11
옵션을 넣는다면 const는 var로 arrow function은 일반 함수로 바뀌게 된다
...
presets: [
['@babel/preset-env',{
targets: {
chrome: '89',
ie: '11',
}
}]
...
폴리필
app.js의 내용을 바꿔보자
//app.js
new Promise();
이 코드를 바벨로 빌드하면
new Promise();
변환되지 않고 똑같은 코드가 나오게된다
그런데 이것을 IE환경에서 돌리면 Promise를 지원하지 않아서 에러가 발생한다
어찌된 일일까?
preset-env가 설정된 바벨은 ECMASCript2015+로 작성된 코드들을 ECMAScript5으로 변환이 가능할 때만 변환해준다
const는 var로 arrow function은 일반 함수로,
하지만 ECMAScript5에서는 Promise를 지원하지 않기 때문에 Promise는 변환할 수가 없다
이럴 때 필요한게 폴리필이다
ECMAScript5가 지원하지 않는 기능을 코드를 추가해서 구현하는 것
타겟 브라우저를 설정한 것 처럼 옵션 객체를 설정해보자
useBuiltIns: 'usage',
corejs: {
version:2,
}
useBuiltIns는 폴리필을 어떻게 적용할지 설정하는 옵션인데 명시하지 않는다면 기본값인 false로 설정된다
그렇기 때문에 빌드할 때 폴리필을 하지 않았던 것
그 아래에 폴리필시에 사용할 패키지 명을 적어주면 빌드시에 corejs를 가져와서 폴리필한다
이제 다시 빌드 결과물을 보자
require("core-js/modules/es6.object.to-string.js");
require("core-js/modules/es6.promise.js");
new Promise();
이전과 다르게 corejs 패키지를 임포트하는 구문이 추가된 것을 볼 수 있다
'Frontend' 카테고리의 다른 글
프론트엔드 개발환경의 이해 : lint (0) | 2021.04.07 |
---|---|
프론트엔드 개발환경의 이해 : Webpack에서 바벨 사용하기 (0) | 2021.04.05 |
주요 플러그인 (0) | 2021.03.18 |
주요 로더와 순서 (0) | 2021.03.16 |
프론트엔드 개발환경의 이해 : Webpack (2) (0) | 2021.03.14 |