Babel 이거 왜 쓰는거?

Nine·2022년 10월 24일
0
post-thumbnail

왜 바벨?

평소 babel이 단순히 코드 변환을 위해서 사용되는것은 알고 있었어요.

하지만 프로젝트에서 babel.config.json 파일을 사용할때 이 속성들은 뭐지?

webpack에서 babel-loader와 ts-loader를 사용하는데 이건 무슨 차이지?

이러한 궁금증들이 계속 쌓여있었거든요.

🤦‍♀️ 이런 설정 파일들에 대한 고민을 깊게 해본 적이 없더라구요.

그래서 바벨에 대한 공부를 하게 되었어요.


바벨이란 (현재 7.19 기준)

Babel is a toolchain that is mainly used to convert ECMAScript 2015+ code into a backwards compatible version of JavaScript in current and older browsers or environments.

ES6 이상의 코드들을 구형 브라우저나 환경에서 동작 가능한 이전 javascript 버전으로 변환하는 도구입니다.

ES6가 2015년 6월에 출시되었습니다.

당연히 브라우저들은 이 최신 문법들을 각 브라우저에 적용시키기 위한 기가이 필요했어요.

그럼 아래처럼 브라우저가 적응하는 기간동안 웹 개발을 진행할 때 ES6 문법을 사용하지 못했을까요?

그렇지 않죠. 바벨을 통해 ES6 코드들을 브라우저가 이해할 수 있는 이전 단계의 코드로 변환했기에 ES6 문법을 잘 사용할 수 있었어요.


바벨 사용법

간단한 예시로 화살표 함수를 transpiling하는 과정을 살펴볼게요.

const fn = () => "화살표 함수입니다."

변환

화살표 함수(es6때 나왔어요)를 이전 자바스크립트(es5) 버전으로 다운그레이드시키기 위해서는 다음과 같은 준비물들이 필요해요.

@babel/core : 바벨의 핵심적인 기능

@babel/cli : 터미널에서 바벨을 명령어로 실행

@babel/plugin-transform-arrow-functions : 화살표 함수를 transform하는 플러그인

자~ 명령어를 입력해서 변환시켜 볼까요?

./node_modules/.bin/babel [변환할 파일] --out-dir [변환될 위치] --plugins=@babel/plugin-transform-arrow-functions

결과

짜잔~ 아래처럼 화살표함수가 트랜스파일링 되었습니다.

var fn = function fn() {
  return "화살표 함수입니다.";
}

😅뭔가 불편해

근데... 트랜스파일링하는 과정에서 뭔가 불편하지 않았나요?

es6에서는 이렇게나 많은 새로운 기능들이 추가되었어요.

그렇다면 하나의 기능에 대응하기 위해서 매번 하나의 plugin 설치가 필요하겠죠.

또, 복잡한 명령어는 또 어떤가요? 굉장히 답답합니다...

✌ 이렇게 사용합시다!

  1. 매번 plugin 설치 -> 다양한 플러그인들을 모아놓은 preset을 설치해서 사용합시다!

  2. config 파일을 활용하여 (babel.config.josn, .babelrc.json) 명령어를 쉽게 입력합시다!


Polyfill

🤔 바벨을 설명할 때에는 늘 폴리필이 나오죠? 이건 뭐길래 늘 바벨과 함께 언급되는걸까요?

A polyfill is a piece of code (usually JavaScript on the Web) used to provide modern functionality on older browsers that do not natively support it.

공식문서에서는 구형 브라우저가 지원하지 않는 최신 기능을 제공하는 코드 뭉치라고 설명하고 있어요.

폴리필은 언어적으로 충전솜이라는 뜻이예요.

인형에 솜이 꺼져서 부족함을 채워주는 그런 솜이라고 볼 수 있겠죠?

그렇다면 바벨에서의 폴리필은 어떤 의미일까요?

Polyfill이 필요한 경우

빌드 과정에서 바벨의 프리셋과 플러그인들 통해 코드 트랜스파일링을 진행하더라도 Promise같은 빌트인, Array.includes 같은 인스턴스 메서드가 코드에 그대로 남아 있을 수 있어요.

그렇다면 해당 빌트인 또는 메서드를 지원하지 않는 환경에서는 에러가 발생할 수 밖에 없겠죠?

이를 해결하기 위한 기술이 폴리필 (Polyfill) 입니다.

✨ 바벨 설정에서 폴리필을 설정하면, 최신 ECMAScript 환경을 만들기 위해 코드가 실행되는 환경에 존재하지 않는 빌트인 메서드 등을 추가해줍니다.

  • 그렇다면 이제 왜 충전솜이라는지 알겠어요.
  • 코드가 실행되는 환경에서 꺼진 솜들을(지원하지 않는 빌트인 메서드) 채워넣어주는 역할을 하는 것이 polyfill이네요.

polyfill의 코드 뭉치인 @core-js

바벨 7.4 버전부터 @babel-polyfill을 대신해서 @core-js가 현재 사용되고 있어요.

@core-js의 실제 코드를 살펴볼까요? array의 include, indexOf 메서드 코드입니다.

살펴보면 var, if, while, 함수 선언형처럼 구형 환경에서도 돌아갈 수 있는 코드뭉치라고 할 수 있겠네요. (충전솜으로 딱이죠?)

이런 충전솜 모듈을 아래처럼 export하고 있습니다.

var entryUnbind = function (CONSTRUCTOR, METHOD) {
  return uncurryThis(global[CONSTRUCTOR].prototype[METHOD]);
}

// Array의 indexOf를 export할 때
module.exports = entryUnbind('Array', 'indexOf');

entryUnbind를 통해 특정 객체(Array), 메서드(indexOf)를 인자로 주게 되는데요, global에서 해당 메서드가 존재하는지 확인하는 과정을 거쳐 메서드를 반환하게 됩니다.

자세한 코드는 core-js github 에서 확인할 수 있어요.

✌ 참고 (uncurryThis가 뭐지?)

Uncurrying this means: Given a method with the signature

obj.foo(arg1, arg2)

transform it into a function with the signature

foo(obj, arg1, arg2)

Polyfill 사용법

자 그럼 Polyfill을 바벨에서 어떻게 사용해야 좋을지 봅시다!

1. core-js 단독 사용

👍 인스턴스 메서드에 대한 동작을 지원합니다.

[1, 2, 3].include(...); // 충전솜 넣어줄게~

😅 하지만 전역 스코프가 오염됩니다.

  • 전역 스코프가 오염된다면  이름 충돌이 발생하고, 예측하기 힘든 에러가 발생할 수 있습니다. 
  • 특히 다른 사용자가 사용할 수 있도록 하는 라이브러리이거나 코드가 실행될 환경을 정확하게 제어할 수 없는 경우 문제가 됩니다.

2. @babel/plugin-transform-rutime + core/js@3

👍 인스턴스 메서드에 대한 동작을 지원합니다.

👍 스코프도 오염되지 않아요.

이렇게만 사용하면 되겠네요! 해결 끝~ 일까요?

끝일 수도 있지만 아닐 수도 있습니다.

만약 프로젝트 내 패키지(서드 파티 라이브러리) 중에서 전역 스코프에 의존하는 패키지가 있다면 어떨까요? 당연히 해당 패키지들도 트랜스파일링을 해줘야겠죠?

예를 들면 Axios처럼 전역으로 Promise같은 es6+ 객체(Map, Set 등...)를 사용하는 경우가 있기에 이런 패키지들을 포함해서 transpiling을 해야합니다.

3. @babel/plugin-transform-rutime + core/js@3 + babel.config.json까지 사용

답은 config 설정 파일에 있어요.

babel.config.json

  • 여러 패키지 디렉토리를 가진 프로젝트에서 하나의 바벨 설정을 적용하고 싶을 때

  • node_modules에도 적용하고 싶을 때

babelrc.json

  • 프로젝트 내에 서드 파티 라이브러리가 바벨에 의해 트랜스폼되기를 바라지 않는 경우

  • 특정 부분만 적용하고 싶은 경우

😛babel.config.json 을 사용하여 프로젝트를 진행하면 좋을 것 같네요.


babel-loader vs ts-loader

현대 웹개발에서는 각 모듈들간의 의존성을 파악하고 최적화하여 몇 개의 압축 파일로 만들어주는 모듈 번들러를 많이 사용합니다. (대표적으로 Webpack이 있죠)

Webpack과 babel을 이어주는 것이 babel-loader입니다.

  • 2018년 Babel7이 나오면서부터 바벨을 이용하는 프로젝트는 복잡한 typescript compiler 필요없이 ts를 사용할 수 있게 되었어요.
  • 그렇다면 babel-loader와 ts-loader는 어떠한 차이점들이 있을까요?

Type checking

const str: string = 9;

babel-loader : 타입 체킹을 진행하지 않아서 에러가 발생하지 않아요. (tsconfig? 그게 뭔데 사뿐히 무시)

ts-loader : tsconfig 파일의 compileOptions 속성을 참고하여 컴파일을 진행하기 때문에 타입 체킹을 엄격하게 해줍니다. 에러가 발생해요.

문법적인 부분

enums

babel-loader : 하나의 파일에 대해서 컴파일을 진행해요.

ts-loader : 프로젝트 전체를 기준으로 컴파일을 진행해요.

따라서, 바벨은 enums처럼 여러 파일을 읽어야하는 경우를 지원하지 못해요.

👏 하지만! (2021.7.26 출시된) Babel 7.15버전부터는 모든 TS 코드 베이스를 컴파일할 수 있습니다.

Decorator

Typescript의 decorator 아직까지도 많은 변화가 있는 기능이예요.

Babel은 ECMAscript spec 기준이다보니 decorator를 typescript가 원하는대로 완전히 컴파일하지 못해요.

👏 하지만! 관련 플러그인을 사용하면 이 또한 가능합니다.
관련 플러그인

빌드 속도 차이

제 팀프로젝트 달록을 기준으로 속도 차이를 비교해봤습니다.

생각보다 유의미한 속도 차이가 발생했네요?

역시 babel-loader는 ts를 컴파일해주기는 하지만 타입체킹을 무시하기 때문에 속도가 훨씬 빠르네요.

🌈 하지만 이 외에도 esbuild-loader, fork-ts-checker-webpack-plugin 등 다양한 loader와 plugin들이 존재해서 장단점을 비교하여 본인만의 빌드 전략을 꾸리면 좋을 것 같아요.

참고자료

https://github.com/zloirock/core-js
https://babeljs.io/doc
https://webpack.js.org/loaders/babel-loader/
https://jeonghwan-kim.github.io/dev/2021/03/08/babel-typescript.html
https://github.com/TypeStrong/ts-loader#hot-module-replacement
https://devblogs.microsoft.com/typescript/typescript-and-babel-7/
https://dev.to/alexeagleson/building-a-modern-web-stack-babel-3hfp#polyfills
https://blog.outsider.ne.kr/1176
https://github.com/babel/babel

profile
함께 웃어야 행복한 개발자 장호영입니다😃

0개의 댓글