Babel과 Polyfill
Babel의 문법 변환과 Polyfill의 런타임 API 보완을 구분해 최신 JavaScript의 브라우저 호환 범위를 설계합니다.
현대 JavaScript는 ES6(ECMAScript 2015) 이후 const, let, 화살표 함수(=>), async/await, class, 스프레드 문법(...) 등 강력한 기능이 빠르게 추가되어 왔습니다.
React, Vue 같은 최신 프레임워크도 이러한 문법을 적극 활용합니다.
하지만 모든 사용자의 브라우저가 최신 JavaScript 문법을 지원하는 것은 아닙니다.
특히 구형 브라우저나 일부 모바일 브라우저에서는 최신 문법을 인식하지 못하여 웹 애플리케이션이 제대로 동작하지 않는 문제가 발생할 수 있습니다.
이러한 호환성 문제를 해결하기 위해 필수적으로 사용되는 도구가 바로 Babel과 Polyfill입니다.
이번 장에서는 Babel이 최신 JavaScript를 구형 브라우저 호환 코드로 변환하는 방식과, Polyfill이 필요한 이유 및 사용법을 정리합니다.
이를 통해 최신 문법으로 개발하면서도 다양한 사용자 환경에서 안정적으로 동작하는 애플리케이션을 만들 수 있습니다.
Babel은 최신 문법을 대상 브라우저가 이해할 수 있는 문법으로 변환하고, Polyfill은 Promise나 Array 메서드처럼 런타임에 없는 기능을 보완합니다.
- 1Babel
최신 JavaScript를 구형 환경용 코드로 변환합니다.
- 2Polyfill 개념
폴리필은 구형 브라우저에 없는 기능을 보완합니다.
- 3Babel·Polyfill 적용 흐름의 관계 요약
Babel은 문법을 변환하고, Polyfill은 런타임 기능을 보완합니다.
- 4Babel·Polyfill 정리
문법 변환과 런타임 보완 범위를 분리해 번들 낭비를 줄입니다.
- 5변환 대상과 런타임 보강을 나누는 법
Babel은 class, optional chaining 같은 문법 형태를 낮은 버전의 JavaScript 표현으로 바꿉니다.
Babel 이란?
Babel은 최신 JavaScript (ES2015+) 코드를 구형 JavaScript 환경(주로 ES5)에서 동작할 수 있도록 변환해주는 트랜스파일러(Transpiler)입니다.
- Transpiler (트랜스파일러): 소스 코드(Source Code)를 다른 소스 코드(Source Code)로 변환하는 도구를 의미합니다. 컴파일러(Compiler)가 고수준 언어를 저수준 언어(기계어)로 변환하는 것과 구분됩니다.
Babel의 필요성
- 브라우저 호환성: 모든 브라우저가 최신 JavaScript 문법을 동시에 지원하지 않습니다. Babel은 개발자가 편리한 최신 문법으로 코드를 작성하고, 실제 배포 시에는 넓은 범위의 브라우저에서 동작하도록 변환해줍니다.
- 생산성 향상: 개발자는
async/await, 화살표 함수 등 생산성을 높여주는 최신 문법을 걱정 없이 사용할 수 있습니다. - 프레임워크 지원: React의 JSX 문법(
<div>Hello</div>와 같은 HTML 유사 코드)은 JavaScript 표준 문법이 아닙니다. Babel은 JSX를 일반 JavaScript 함수 호출(React.createElement('div', null, 'Hello'))로 변환하여 브라우저가 이해할 수 있도록 합니다. - TypeScript 지원: TypeScript 코드를 JavaScript 코드로 변환하는 데도 Babel을 사용할 수 있습니다.
Babel의 동작 방식 (간략)
Babel과 Polyfill에서는 변경 관리, 도구 설정, 협업 확인 절차를 정리합니다.
Babel은 최신 문법을 대상 환경이 이해할 코드로 바꾸지만 Promise나 fetch 같은 런타임 API를 만들지는 않는다.
| 단계 | 하는 일 | 예시 | 주의점 |
|---|---|---|---|
| Parse | 소스 코드를 AST로 읽음 | arrow function, class field | 문법 자체를 못 읽으면 실패 |
| Transform | plugin과 preset이 AST를 바꿈 | optional chaining 변환 | 대상 환경 기준이 필요 |
| Generate | 변환된 AST를 JS 코드로 출력 | target JS | source map과 디버깅 확인 |
| Preset | 여러 변환 규칙 묶음 | preset-env | browserslist 품질에 의존 |
| Polyfill boundary | 없는 API는 별도 보강 필요 | Promise, fetch, Array.from | 문법 변환과 API 보강 혼동 |
Babel은 플러그인(Plugins)과 프리셋(Presets)이라는 개념을 사용합니다.
-
플러그인(Plugins)
- Babel의 가장 작은 변환 단위입니다. 특정 JavaScript 문법 하나를 변환하는 규칙을 정의합니다.
- 예:
babel-plugin-transform-es2015-arrow-functions는 화살표 함수를 일반 함수로 변환합니다.
-
프리셋(Presets)
- 여러 개의 플러그인들을 미리 묶어놓은 것입니다. 개발자가 개별 플러그인을 일일이 설정할 필요 없이, 특정 목적(예: 특정 ECMAScript 버전 지원)을 위해 필요한 플러그인들을 한 번에 적용할 수 있게 해줍니다.
@babel/preset-env: 가장 널리 사용되는 프리셋입니다. 타겟으로 하는 브라우저 환경(.browserslistrc파일이나package.json의browserslist필드에 명시)에 맞춰 필요한 플러그인들을 동적으로 결정하여 적용합니다. 불필요한 변환을 줄여 번들 크기를 최적화합니다.@babel/preset-react: React의 JSX 문법을 JavaScript로 변환하는 데 사용되는 플러그인들을 포함합니다.@babel/preset-typescript: TypeScript 코드를 JavaScript로 변환하는 데 사용됩니다.
Webpack과 Babel 연동
Webpack 프로젝트에서 Babel을 사용하려면 babel-loader를 설치하고 webpack.config.js에 설정해야 합니다.
npm install --save-dev @babel/core @babel/preset-env @babel/preset-react babel-loader.babelrc 파일 생성
루트 디렉토리에 .babelrc 파일을 생성하고 사용할 프리셋을 정의합니다.
(이 파일은 babel-loader에 의해 자동으로 로드됩니다.)
// .babelrc
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}@babel/preset-env는 기본적으로 브라우저 환경을 가정하지만, 더 구체적으로 타겟 브라우저를 명시할 수 있습니다.
(예: package.json에 browserslist 필드 추가)
{
"browserslist": [
"defaults",
"not IE 11",
"last 2 versions",
"> 1%"
]
}기존 package.json에 browserslist 키를 병합합니다.
이 예시는 IE 11을 제외하고 최신 두 버전과 점유율 1% 초과 브라우저를 대상으로 합니다.
webpack.config.js 설정
module.rules에 babel-loader를 추가합니다.
// webpack.config.js (일부)
module.exports = {
// ...
module: {
rules: [
{
test: /\.jsx?$/, // .js 또는 .jsx 파일
exclude: /node_modules/, // node_modules 내부 파일은 변환하지 않음
use: {
loader: 'babel-loader',
// options는 .babelrc 파일로 대체 가능. (없을 경우 여기에 직접 작성)
// options: {
// presets: ['@babel/preset-env', '@babel/preset-react'],
// },
},
},
],
},
// ...
};이렇게 설정하면 Webpack이 JavaScript 파일을 번들링하기 전에 babel-loader를 통해 Babel이 최신 문법을 구형 문법으로 변환하게 됩니다.
Polyfill 이란?
Polyfill (폴리필)은 구형 브라우저가 지원하지 않는 최신 JavaScript 기능(메서드, 객체 등)을 사용 가능한 형태로 구현해 놓은 코드 조각입니다.
Babel이 문법(Syntax)을 변환한다면, Polyfill은 코드의 기능(API)을 제공합니다.
Polyfill의 필요성
- Babel은
const, 화살표 함수와 같은 문법적(Syntax) 인 부분을 변환합니다. - 하지만
Promise,fetchAPI,Array.prototype.includes등은 새로운 전역 객체나 메서드(API)입니다. 이러한 API들은 특정 브라우저 환경에 내장되어 있지 않으면 Babel만으로는 해결할 수 없습니다. - Polyfill은 이러한 누락된 API들을 구형 브라우저에서도 동작하도록 메꿔주는 역할을 합니다.
Polyfill 사용 방법
가장 흔하게 사용되는 Polyfill은 @babel/polyfill (구 버전) 또는 core-js와 regenerator-runtime입니다.
최신 Babel 버전에서는 @babel/polyfill이 deprecated 되었고 core-js를 직접 사용하는 것을 권장합니다.
core-js 및 regenerator-runtime 설치
core-js는 다양한 최신 ECMAScript 기능의 Polyfill을 제공하며, regenerator-runtime은 async/await와 같은 Generator 기반의 비동기 코드를 변환할 때 필요합니다.
npm install --save core-js regenerator-runtime참고: 이 패키지들은 실제 서비스에서 필요하므로 dependencies에 설치합니다.
엔트리 파일에 Polyfill 임포트 (직접 임포트 방식)
프로젝트의 메인 엔트리 파일(예: src/index.js)의 가장 상단에 필요한 Polyfill을 임포트합니다.
// src/index.js
import 'core-js/stable'; // 모든 안정화된 ECMAScript 기능 Polyfill
import 'regenerator-runtime/runtime'; // async/await (Generator) Polyfill
import React from 'react';
import ReactDOM from 'react-dom/client';
// ... 나머지 코드이 방식은 필요한 모든 Polyfill 코드를 번들에 포함시키므로, 번들 크기가 커질 수 있습니다.
@babel/preset-env와 useBuiltIns 옵션 활용 (권장 방식)
@babel/preset-env의 useBuiltIns 옵션을 사용하면, Babel이 트랜스파일링하는 과정에서 실제로 사용된 최신 JavaScript 기능 중 타겟 브라우저에서 지원하지 않는 것들만 자동으로 Polyfill을 삽입하도록 할 수 있습니다.
이는 번들 크기를 최적화하는 가장 좋은 방법입니다.
useBuiltIns: "usage": 가장 권장되는 방식입니다. 코드에서 사용된 기능만 체크하여 필요한 Polyfill을 자동으로 삽입합니다. (따로import 'core-js'할 필요 없음)useBuiltIns: "entry": 엔트리 파일에import 'core-js'를 명시적으로 작성하고,@babel/preset-env가 타겟 브라우저에 맞춰 필요한 Polyfill만 로드합니다. (이 경우core-js/stable전체를 임포트하는 것보다 효율적)
.babelrc 설정// .babelrc
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage", // 사용된 기능에 따라 Polyfill 자동 삽입
"corejs": { "version": 3, "proposals": true } // core-js 버전 지정 및 proposal 기능 포함 여부
}
],
"@babel/preset-react"
]
}이 설정은 browserslist 설정과 함께 동작하여, 타겟 브라우저에 따라 필요한 Polyfill만 자동으로 포함시킵니다.
개발자는 import 'core-js/stable' 등을 직접 작성할 필요가 없어집니다.
Babel과 Polyfill의 관계 요약
| 역할 | Babel (트랜스파일러) | Polyfill (런타임 주입) |
|---|---|---|
| 대상 | 최신 문법(Syntax) | 최신 기능/API(Feature/API) |
| 예시 | 화살표 함수, const, let, class, JSX | Promise, fetch, Array.prototype.includes, Map |
| 방식 | 코드 자체를 변환하여 구형 문법으로 변경 | 누락된 기능을 구현한 코드를 런타임에 추가 |
| 결과 | 문법 오류 해결 | ReferenceError, TypeError 같은 런타임 오류 해결 |
함께 사용하는 이유: 대부분의 모던 웹 프로젝트는 최신 JavaScript 문법을 사용하고 새로운 API도 활용하므로, Babel과 Polyfill을 함께 사용하여 브라우저 호환성 문제를 완벽하게 해결해야 합니다.
babel-loader와 @babel/preset-env의 useBuiltIns: "usage" 옵션이 이 둘을 효율적으로 통합하여 관리하는 가장 일반적이고 권장되는 방식입니다.
호환성 설정은 한 번 정하고 끝나는 값이 아니라, 지원 브라우저와 번들 크기 사이에서 계속 조정하는 기준입니다.
Babel은 코드 모양을 바꾸고, Polyfill은 실행 환경에 없는 기능을 채워 넣습니다. 오류 종류를 보면 필요한 처방이 달라집니다.
- 오류 종류
보면 필요한 처방이 달라집니다.
- 번들 크기 전체 Polyfill보다
Babel·Polyfill 정리
정리 단계에서는 변환 대상 문법과 런타임 보완 범위를 분리해 번들 정책을 결정하는 것이 중요합니다.
이번 장에서는 모든 브라우저 환경에서 안정적으로 동작하는 웹 애플리케이션을 만들기 위한 핵심 도구인 Babel과 Polyfill에 대해 학습했습니다.
이번 절에서는 Babel이 최신 JavaScript 문법과 JSX를 대상 브라우저에 맞는 코드로 변환하는 트랜스파일러라는 점을 정리했습니다.
플러그인, 프리셋, babel-loader는 변환 범위와 Webpack 연동을 설정하는 기준입니다.
이어서, Babel만으로는 해결할 수 없는 최신 JavaScript API 부족 문제를 해결하기 위한 Polyfill의 필요성을 이해하고, core-js를 활용하여 Polyfill을 적용하는 방법을 익혔습니다.
특히, @babel/preset-env의 useBuiltIns: "usage" 옵션을 사용하여 필요한 Polyfill만 자동으로 삽입하여 번들 크기를 최적화하는 전략을 살펴보았습니다.
Babel과 Polyfill은 문법 변환과 API 보완을 나누어 브라우저 호환성을 맞추는 도구입니다.
대상 브라우저, 번들 크기, 필요한 런타임 기능을 기준으로 설정해야 합니다.
Babel과 Polyfill은 지원 브라우저, 문법 변환, API 보완 범위를 함께 조정해야 번들 크기와 호환성을 균형 있게 맞출 수 있습니다.
문법, 기능, 대상 같은 기준을 실제 브라우저 흐름에서 나눠 확인해야 구현 기준과 실패 대응이 흐려지지 않습니다.
- 문법
Babel은 최신 JS 문법을 오래된 브라우저가 이해할 코드로 바꿉니다.
- 기능
Polyfill은 Promise, fetch 같은 없는 API를 채워 넣습니다.
- 대상
browserslist로 지원 브라우저 범위를 명시합니다.
- 검증
실제 대상 환경에서 빌드 결과와 런타임 오류를 확인합니다.
optional chaining 같은 syntax는 Babel이 바꿀 수 있지만 Promise, fetch, Array.prototype.at은 polyfill 전략이 필요하다.
| 구분 | 해결하는 문제 | 대표 도구 | 검증 기준 |
|---|---|---|---|
| Syntax | 브라우저가 새 문법을 파싱하지 못함 | Babel plugin, preset-env | 구형 브라우저에서 문법 오류 없음 |
| Target | 지원 브라우저 범위 결정 | browserslist | 실제 사용자 환경과 일치 |
| Runtime API | 실행 환경에 없는 객체와 메서드 | core-js, fetch polyfill | API 호출이 존재함 |
| Injection | polyfill을 어디에 넣을지 결정 | usage, entry, manual | 중복 주입과 번들 증가 방지 |
| Cost | 불필요한 변환과 보강 제거 | modern build, differential serving | 최신 브라우저에 과한 코드 전송 금지 |
Babel과 polyfill을 같은 도구처럼 쓰면 구형 브라우저에서 문법은 통과하지만 실행 중 API 오류가 날 수 있다.
| 항목 | 맡는 역할 | 예시 | 체크 포인트 |
|---|---|---|---|
| Babel | 새 문법을 오래된 문법으로 변환 | arrow, class, optional chaining | DOM API를 만들지는 않음 |
| Preset-env | 브라우저 목표에 맞춰 변환 선택 | browserslist | 지원 범위가 실제와 맞는가 |
| Polyfill | 런타임에 없는 객체와 메서드 제공 | Promise, Set, Array methods | 전역 오염과 중복 확인 |
| core-js | 표준 API 구현 제공 | useBuiltIns policy | 필요한 API만 들어가는가 |
| Test | 최저 지원 브라우저에서 실행 확인 | 번들 크기, 기능 테스트 | 모던 번들과 구형 번들 분리 검토 |