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가 협력해 비동기 로직을 처리하기 때문입니다.
-