1. Babel이란
Babel은 최신 JavaScript 문법을 구형 브라우저에서도 실행 가능하게 변환해주는 트랜스파일러(transpiler) 입니다. 이를 통해 개발자는 최신 문법으로 코드를 작성하면서도, 다양한 환경에서 실행될 수 있도록 도와줍니다.
- Transpiler: Babel은 코드를 다른 형태로 변환해 같은 수준의 언어로 바꿉니다. 엄밀히 말하면 트랜스파일러지만, 컴파일러의 일종으로 볼 수 있습니다.
- 최신 JavaScript 지원: Babel을 사용하면 구형 브라우저 호환성을 걱정하지 않고 최신 문법을 자유롭게 사용할 수 있습니다.
2. Babel의 동작 과정
Babel은 세 가지 주요 단계를 통해 코드를 변환합니다
1. 파싱(Parsing)
- Babel은 소스 코드를 AST(Abstract Syntax Tree, 추상 구문 트리)로 변환합니다.
- AST는 코드의 구조를 트리 형태로 표현한 것이며, 이를 기반으로 코드가 해석됩니다.
2. 변환(Transform)
- 생성된 AST를 사용해 개발자가 설정한 플러그인과 프리셋에 따라 AST를 수정하여 변환 작업을 수행합니다.
3. 코드 생성(Generate)
- 변환된 AST는 다시 코드로 변환되어, 하위 호환성을 가진 새로운 코드가 만들어집니다.
AST란
AST(추상 구문 트리)는 소스 코드의 구조를 트리 형태로 나타낸 것입니다. Babel은 이 AST를 바탕으로 코드를 분석하고 변환합니다.
- 코드의 구성 요소(변수, 함수, 연산자 등)가 트리의 노드로 표현됩니다.
- AST는 코드의 구조를 명확하게 이해하고 변형하는 데 중요한 역할을 합니다.
Babel 설정 파일 (Babel Config)
Babel은 설정 파일을 통해 플러그인과 프리셋을 제어할 수 있습니다. 가장 많이 사용되는 두 가지 설정 파일은 다음과 같습니다
babel.config.json
-
루트 폴더에 생성되며, 프로젝트 전체에 적용됩니다.
-
Monorepo
와 같은 구조에서 유용하며, 전체 프로젝트에서 통일된 설정을 적용할 수 있습니다. -
Babel 7부터
.babelrc
대신babel.config.json
을 권장합니다.{ "presets": [ [ "@babel/env", { "targets": { "edge": "17", "firefox": "60", "chrome": "67", "safari": "11.1" } } ] ] }
.babelrc.json
- 프로젝트의 특정 파일이나 폴더에만 적용되는 설정 파일입니다.
- 개별 패키지나 모듈에 맞춘 설정이 필요할 때 사용합니다.
Babel과 Webpack 연동
Babel을 Webpack과 함께 사용하려면 babel-loader
를 설치하여 사용합니다.
1.Babel Loader 설정
babel-loader
는 Babel을 Webpack의 로더로 실행시키는 역할을 합니다.- Webpack 설정 파일에서 Babel을 설정한 후, 프로젝트 번들링 시 Babel이 코드 트랜스파일링을 진행합니다.
node_modules
는 트랜스파일링이 필요 없으므로, exclude 옵션으로 제외해야 성능을 최적화할 수 있습니다.// webpack.config.js module.exports = { module: { rules: [ { test: /\.(tsx|ts)$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: [ '@babel/preset-env', '@babel/preset-react', '@babel/preset-typescript', ], plugins: [ '@babel/plugin-transform-runtime', 'babel-plugin-styled-components', ], }, }, }, ], }, };
Babel의 플러그인(Plugins)과 프리셋(Presets)
1. 플러그인(Plugins)
- Babel에서 코드 변환은 플러그인을 통해 이루어집니다.
- 각 플러그인은 Babel 컴파일 과정에서 AST를 변형하는 역할을 하며, 변형된 AST를 가지고 타겟 코드를 생성합니다.
2. 프리셋(Presets)
- 프리셋은 플러그인들의 집합입니다.
- 여러 플러그인을 하나씩 추가하는 대신, 그룹으로 묶어 한 번에 설정할 수 있습니다.
- 대표적인 Preset들:
@babel/preset-env
: 최신 자바스크립트 문법을 ES5로 변환@babel/preset-react
: React의 JSX를 변환@babel/preset-typescript
: TypeScript 문법 지원
각 프리셋은 해당 문법을 변환하는 데 필요한 플러그인들을 포함하고 있습니다.
Babel과 React
Babel을 사용하면 React의 JSX 문법도 트랜스파일링할 수 있습니다.
-
@babel/preset-react
은 JSX 코드를 React의React.createElement
함수 호출로 변환합니다.// babel 컴파일 전 const profile = ( <div> <img src='profile.png' className='profile' /> <h1>{[user.firstName, user.lastName].join(' ')}</h1> </div> );
// babel 컴파일 후 const profile = React.createElement( 'div', null, React.createElement('img', { src: 'profile.png', className: 'profile' }), React.createElement('h1', null, [user.firstName, user.lastName].join(' ')) );
Babel과 TypeScript
Babel은 TypeScript를 트랜스파일링할 수도 있습니다.
@babel/preset-typescript
를 사용하여 TypeScript 코드를 변환할 수 있지만, 타입 체킹은 Babel에서 지원하지 않습니다.- 타입 체킹은
ts-loader
와 같은 별도의 도구를 사용하여 처리해야 하며, Babel과 함께 사용하는 경우fork-ts-checker-webpack-plugin
과 같은 플러그인이 필요합니다.
Polyfill과 Babel
Babel은 최신 자바스크립트 문법을 변환하지만, 모든 최신 기능을 지원하는 것은 아닙니다. 특히, Promise
나 WeakMap
같은 최신 객체는 변환되지 않으므로 Polyfill
이 필요합니다.
1. Polyfill이 필요한 이유
- 구형 브라우저에서는 최신 문법과 객체를 지원하지 않기 때문에, 런타임에 특정 기능을 추가해야 합니다.
2. @babel/polyfill
-
과거에는
@babel/polyfill
이 사용되었으나, 최근에는@babel/plugin-transform-runtime
과core-js@3
를 사용하는 방식이 권장됩니다.// webpack.config.js 설정 module.exports = { plugins: [ [ '@babel/plugin-transform-runtime', { corejs: 3, // core-js 설정 regenerator: true, }, ], ], };
Babel에서 Promise 처리
Babel은 ES6에서 등장한 Promise를 구형 브라우저에서도 사용할 수 있도록 변환합니다.
-
async/await
문법은generator
와 대응되며,regenerator-runtime
을 통해 변환됩니다.// ES6의 async-await async function testFunc() { let value = await promise; console.log(value); } // Babel로 변환된 코드 function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep( gen, resolve, reject, _next, _throw, 'next', value ); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'throw', err); } _next(undefined); }); }; }
-
async/await
와generator
변환
-
async
키워드는generator
함수로 변환되며,await
키워드는yield
에 대응됩니다. -
Babel
은 이러한 변환을 통해 비동기 코드를 동기적 흐름처럼 사용할 수 있게 합니다.
-
이터레이터 메서드 호출
-
하나의 로직이 종료될 때마다 이터레이터 객체의 메서드인
next()
가 호출되어 다음 로직을 실행합니다. -
next()
메서드는 비동기적 패턴을 관리하는데, 이터레이터의 반환값이 완료 상태(done)라면resolve
로 값을 반환합니다.
-
재귀적
Promise
호출
-
반환값이 완료되지 않은 경우,
Promise
를 다시 재귀적으로 호출하여 비동기 흐름을 처리합니다. -
이때 비동기 처리의 흐름을 제어하는 부분은
Babel
에서 변환된generator
함수가 담당합니다.
-
regenerator
라이브러리 사용
-
ES5에는
generator
가 정의되지 않았기 때문에Babel
은regenerator
라이브러리를 사용해generator
를 흉내낸 함수를 구현합니다. -
여기서
Babel
이 생성하는 함수가_asyncToGenerator
입니다.
-
Promise
와generator
의 협력
generator
는 비동기적인 패턴을yield
를 통해 동기적인 흐름처럼 처리할 수 있도록 바꿔줍니다.Promise
는generator
로 만들어진 이터레이터를 반복해서 실행하여 비동기 흐름을 관리합니다.await
키워드가 사용된 함수는 항상Promise
를 반환해야 하는 이유도, 이처럼generator
와Promise
가 협력해 비동기 로직을 처리하기 때문입니다.
-