Babel 복기 하기

윤여찬's avatar
Feb 28, 2025
Babel 복기 하기
vue2를 사용한던게 벌써 어엿 3년이 넘어간다… 그래서 babel 오류가 뜨는데 어? 하고 했었는데 하는 휴먼 에러가 발생하여 babel을 조금 더 확실히 개념을 잡고 넘어가고자 한다.
 

Babel 이란?

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
Babel은 주로 현재 및 구형 브라우저나 환경에서 ECMAScript 2015+ 코드를 이전 버전의 JavaScript로 변환하는 데 사용되는 도구 체인입니다
 
공식 문서를 보면 이렇게 나와있다. ES6가 나온건 2015.06이었다. 하지만 각 브라우저들의 대응은 아래와 같이 이루어졌는데
 
크롬 - 2016.05
파이어폭스 - 2017.03
엣지 - 2016.08
사파리 - 2016.09
익스플로워 - 응 안돼
 
이렇게 각 브라우저들 마다의 대응이 나온 건 15년도지만 각각의 브라우저의 대응은 늦었다. 그럼? es6의 문법은 사용할 수 없었던 것일까~?
 
아니다. 이때 바벨을 이용해서 다운그레이드를 해서 사용할 수 있도록했다.
 
한가지 예시를 들면 화살표 함수가 있다.
const fn = () => "babel is ECMAScript";
이렇게 화살표 함수를 사용하기 위해선
 
@babel/core: 바벨의 핵심적인 기능
@babel/cli: 터미널로 바벨을 사용
@babel/plugin-transform-arrow-functions: 화살표 함수를 transform하는 플러그인
@babel/plugin-transform-classes: ES6 클래스 → ES5 생성자 함수 변환
@babel/plugin-transform-template-literals: 템플릿 문자열 → 일반 문자열 변환
@babel/plugin-transform-block-scoping: letconstvar 변환
 
이렇게 핵심적인 바벨의 요소들이 필요하다.
./node_modules/.bin/babel [변환할 파일] --out-dir [변환될 위치] --plugins=@babel/plugin-transform-arrow-functions
위와 같이 명령어를 입력할 때 해당 명령어 처럼 넣어주게 된다면 아래 처럼 변환되는 것을 확인할 수 있다.
const fn = () => "babel is ECMAScript"; var fn = function fn() { return "babel is ECMAScript"; }
근데 이런 과정에 이렇게 번거롭게 모든 ES6에 추가된 아래의 기능들을 다 바꿔주기엔 무리가 있지 않을까~?

String

String.includes ()

String.startWith()

String.endWith()

Array

Array.from()

Array.keys()

Array.find()

Array.findIndex()

Math

Math.trunc()

Math.sign()

Math.cbtr()

Math.log2()

Math.log10()

Number

Number.isInteger()

Number.isSafeInteger()

Global Method

isFinite()

isNaN()

Object

Object.entries()

그래서 보통은 설정 파일을 활용하여 Babel을 사용하는데
{ "presets": [ [ "@babel/preset-env" { "targets": { "edge": "17", "firefox": "60", "chrome": "67", "safari": "11.1" }, "useBuiltIns": "usage", "corejs": "3.6.5" } ] ] }
위와 같이 config 파일을 활용하여 babel.config.json 혹은 .babelrc.json 파일을 활용한다. 그리고 보통 이전 직장에서는 Babel을 단독으로 실행하는 경우는 적고, Webpack과 연동해서 사용하는 경우가 많았는데 되돌아보면 아래 처럼 사용했던 것 같다.
module.exports = { module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: "babel-loader" } } ] } };
  • babel-loader: Babel을 Webpack과 함께 사용할 수 있도록 해주는 로더
  • exclude: /node_modules/: node_modules 폴더 제외 (빌드 속도 향상)
이렇게 설정하면 Webpack 빌드시 Babel이 자동 적용되어 쉽게 사용할 수 있었다.

Babel Presets

위에서 @babel/preset-env를 확인 했는데 Babel에는 여러 가지 Preset이 존재한다.
  • @babel/preset-env: 최신 JavaScript를 구버전으로 변환
  • @babel/preset-react: JSX 문법을 JavaScript로 변환
  • @babel/preset-typescript: TypeScript 문법을 JavaScript로 변환
이 중에서 가장 중요한 것이 @babel/preset-env인데, 해당 옵션을 아래의 코드를 보면 이해가 좀 더 빠르다.
{ "presets": [ [ "@babel/preset-env", { "targets": { "browsers": "> 0.25%, not dead" }, "useBuiltIns": "usage", "corejs": 3 } ] ] }
여기서 targets 옵션의 의미:
  • "browsers": "> 0.25%, not dead" → 시장 점유율 0.25% 이상이며, 지원이 중단되지 않은 브라우저만 타겟으로 변환
useBuiltIns 옵션의 의미:
  • "usage" → 필요한 polyfill만 포함
  • "entry" → 전체 core-js 라이브러리 로드
  • false → polyfill을 포함하지 않음
이런 식으로 Presets(프리셋)은 기본적으로 브라우저나 실행 환경이 지원하지 않는 최신 JavaScript 기능을 자동으로 변환해주는 역할을 한다.

Polyfill

충전솜이라는 뜻을 가지고 있다.
 
부족한걸 채운다고 나는 이해했는데 실제로 이전 직장에서 Promise에 Array.prototype.includes는 트랜스파일링을 안 해주는 경우가 발생했고 이때
Polyfill을 통해 Promise, Array.prototype.includes를 코드 뭉치에서 충전하여 쓰는 역할을 하게 되었다.
 
하지만 이도 추후에 core-js로 변경됬다. 그리고 useBuiltIns 옵션 또한 같이 들어오게 되었다.
 
이안에도 core-js에 방대한 코드들을 다들고 와서 사용 안 하기 위해서 useBuiltIns: entryuseBuiltIns: usage 를 사용해서 불필요한 폴리필을 제외하고 필요한 것만 사용하게 했다.

가장 큰 문제

 
core-js를 사용할 때 제일 큰 문제는 단독으로 사용할 경우 전역 스코프가 오염이 될 가능성이 농후하다는 점이다. 이렇게 되면 문제가 이름 충돌등 끔찍한 사고가… 나타날 수 도 있다..
 
그래서 @babel/plugin-transform-runtme + core-js@3처럼 같이 사용하게 된다.
Babel은 ES6+ 코드를 변환할 때, Helper Function를 코드마다 직접 삽입하는데
class Example { constructor() { console.log("Hello Babel"); } }
Babel이 변환한 결과:
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Example = function Example() { _classCallCheck(this, Example); console.log("Hello Babel"); };
클래스를 정의할 때마다 _classCallCheck() 함수가 코드에 삽입
➡ 이 함수가 파일마다 중복되면 번들 크기가 불필요하게 커지는 문제가 발생하게 됨…
 
@babel/plugin-transform-runtme + core-js@3처럼 플러그인을 사용하면, Helper 함수가 코드마다 직접 삽입되지 않고, @babel/runtime 패키지에서 import됩니다.
{ "plugins": [ ["@babel/plugin-transform-runtime", { "corejs": 3 }] ]
Babel이 변환한 코드 (플러그인 적용 후):
import _classCallCheck from "@babel/runtime/helpers/classCallCheck"; var Example = function Example() { _classCallCheck(this, Example); console.log("Hello Babel"); };
Helper 함수가 직접 삽입되지 않고 @babel/runtime에서 import됨!!!
중복 삽입이 사라져 번들 크기 최적화
전역 오염(Global Pollution) 방지!!!
 
이렇게 오랫만에 babel을 뒤집어봣다. 사실 오랫만에 vue2를 보면서 느끼는 거지만 23년 부로 종료된 vue2를 진짜 종료 시켜버려야 한다고 느꼈다.
 
다시금… vite 만세

기존 Webpack 방식:

  1. 코드 변경 시 전체 프로젝트 다시 빌드 → 오래 걸림 (특히 Vue 2 + Webpack 조합은 정말 느렸음)
  1. 번들링 과정이 복잡하고, 개발 환경에서 느림

Vite 방식:

  1. ESM (ES Modules) 기반 개발 서버 → 필요한 모듈만 로드 (Hot Module Replacement 최적화)
  1. 즉각적인 HMR (Hot Module Replacement) → 코드 변경 후 거의 즉시 반영
  1. Pre-Bundling (esbuild 활용) → 빌드 속도가 Webpack 대비 10~100배 빠름
 
👉 결과적으로, Vue 2(Webpack) 대비 Vue 3(Vite)은 개발 속도가 미친 듯이 빨라짐! 그러니 … vue2 쓰지말자 우리도 wim-x 1.5 버리자
 
Share article

찬찬잉