Babel과 Polyfill
Babel의 문법 변환과 Polyfill의 런타임 API 보완을 구분해 최신 JavaScript의 브라우저 호환 범위를 설계합니다.
현대 JavaScript는 ES6(ECMAScript 2015) 이후 const, let, 화살표 함수(=>), async/await, class, 스프레드 문법(...) 등 강력한 기능이 빠르게 추가되어 왔습니다.
React, Vue 같은 최신 프레임워크도 이러한 문법을 적극 활용합니다.
하지만 모든 사용자의 브라우저가 최신 JavaScript 문법을 지원하는 것은 아닙니다.
특히 구형 브라우저나 일부 모바일 브라우저에서는 최신 문법을 인식하지 못하여 웹 애플리케이션이 제대로 동작하지 않는 문제가 발생할 수 있습니다.
이러한 호환성 문제를 해결하기 위해 필수적으로 사용되는 도구가 바로 Babel과 Polyfill입니다.
이번 장에서는 Babel이 최신 JavaScript를 구형 브라우저 호환 코드로 변환하는 방식과, Polyfill이 필요한 이유 및 사용법을 정리합니다.
이를 통해 최신 문법으로 개발하면서도 다양한 사용자 환경에서 안정적으로 동작하는 애플리케이션을 만들 수 있습니다.
COMPATIBILITY CHECK
문법 변환과 API 보완은 따로 확인하고 함께 검증합니다
같은 코드가 새 문법과 새 API를 동시에 사용할 수 있습니다. 대상 브라우저에서 부족한 기능마다 필요한 수단을 고르고, 최종 번들을 실제 환경에서 시험합니다.
1. 새 문법을 읽지 못함
const add = (a, b) =>
a + b;Babel이 대상 환경에 맞는 문법으로 변환합니다. JSX도 해당 프리셋으로 JavaScript 호출로 바꿉니다.
확인: 브라우저가 번들을 parse하는가?
2. ECMAScript API가 없음
[1, 2].includes(2)
new Promise(resolve =>
resolve(1))필요한 core-js 등 런타임 구현을 포함합니다. 문법을 바꾸는 것만으로 없는 메서드가 생기지는 않습니다.
확인: 실제 API 호출이 동작하는가?
3. Web API가 없음
fetch('/api/data')fetch 등은 별도 polyfill이나 대체 구현을 검토합니다. core-js를 넣었다고 모든 Web API가 제공되지는 않습니다.
확인: 기능·권한·동작 조건을 만족하는가?
대상을 정한 뒤 필요한 항목을 모두 적용
문법 변환 + 필요한 API 보완을 함께 적용할 수 있습니다. 이미 지원하는 기능은 불필요하게 보완하지 않습니다. 지원표와 실제 대상 브라우저의 회귀 테스트로 결과를 확인합니다.
이 절의 설정은 Babel 7 기준
preset-env의 useBuiltIns: usage와 corejs 옵션은 Babel 7 예제입니다. Babel 8에서는 이 옵션이 제거되어 별도 polyfill 플러그인을 사용합니다.
설정으로 해결되지 않는 범위도 확인
프레임워크의 지원 환경과 플랫폼 동작을 함께 확인합니다. TypeScript 프리셋의 타입 제거는 타입 검사를 대신하지 않으며, regenerator helper 필요 여부도 버전·변환 설정에 따라 달라집니다.
Babel이란?
Babel은 최신 JavaScript (ES2015+) 코드를 구형 JavaScript 환경(주로 ES5)에서 동작할 수 있도록 변환해주는 트랜스파일러(Transpiler)입니다.
- Transpiler (트랜스파일러): 소스 코드(Source Code)를 다른 소스 코드(Source Code)로 변환하는 도구를 의미합니다. 소스 간 변환도 넓게는 컴파일의 한 종류이며, 모든 컴파일러가 반드시 기계어만 출력하는 것은 아닙니다.
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의 동작 방식 (간략)
문법을 읽는 단계와 읽은 코드가 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에 설정해야 합니다.
이 절의 useBuiltIns·corejs 예제는 Babel 7 기준입니다. Babel 8에서는 이 두 옵션이 제거되었으므로 babel-plugin-polyfill-corejs3 같은 별도 플러그인 설정이 필요합니다.
npm install --save-dev @babel/core@7 @babel/preset-env@7 @babel/preset-react@7 babel-loader.babelrc 파일 생성
루트 디렉터리에 .babelrc 파일을 생성하고 사용할 프리셋을 정의합니다.
(이 파일은 babel-loader에 의해 자동으로 로드됩니다.)
// .babelrc
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}@babel/preset-env는 기본적으로 브라우저 환경을 가정하지만, 더 구체적으로 타겟 브라우저를 명시할 수 있습니다.
(예: package.json에 browserslist 필드 추가)
{
"browserslist": [
"defaults",
"last 2 versions",
"> 1%",
"not IE 11"
]
}기존 package.json에 browserslist 키를 병합합니다.
이 배열의 양수 쿼리는 합집합으로 대상을 더하고 not IE 11은 IE 11을 제외합니다. defaults의 대상도 포함되므로 실제 지원 목록은 설치된 Browserslist로 npx browserslist를 실행해 확인합니다.
webpack.config.js 설정
module.rules에 babel-loader를 추가합니다. 앞 절의 설정을 이어 쓴다면, 이 절의 .babelrc 설정으로 프리셋을 옮길 때 babel-loader의 options.presets를 제거해 아래처럼 설정 파일을 한 곳에서 읽게 합니다.
// 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은 Generator 기반 변환 코드가 해당 런타임을 참조할 때 사용합니다. 최신 Babel의 helper 처리와 대상 설정에 따라 별도 런타임 필요 여부가 달라질 수 있습니다.
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 7 usage 구성의 범위입니다. 설치한 core-js의 구체적인 minor 버전과 지원 기능을 맞추고, proposals:true를 모든 제안 API가 언제든 제공된다는 뜻으로 읽지 않습니다. preset-env의 버전별 옵션 계약은 Babel 8에서 useBuiltIns·corejs 제거와 별도 polyfill 플러그인을 구분합니다. 따라서 원 예제의 @7 설치 정책과 다음 버전의 설정을 섞지 않습니다.
예시가상의 대상 브라우저가 화살표 문법은 지원하지만 Array.prototype.includes와 fetch는 지원하지 않는다고 정합니다. 이 조건에서 화살표 함수 자체는 변환할 필요가 없고 includes에는 ECMAScript API 구현, fetch에는 별도 Web API 보완을 검토합니다. 화살표를 function 문법으로 바꾸는 작업만으로 두 API가 생기지 않습니다. 지원표는 이 문제의 전제이며 현재 특정 브라우저의 지원 목록을 측정한 값이 아닙니다.
독립 확인다른 대상은 화살표 문법을 읽지 못하고 Promise와 fetch도 없습니다. 어떤 세 종류의 준비가 필요한지 구분하세요. preset-typescript로 타입 표기를 제거한 성공만으로 타입 오류가 없다는 검사가 끝났다고 할 수 있나요? 설정의 대상 목록을 바꾼 뒤 예전 bundle.js만 시험하면 바뀐 정책을 검증한 것인가요?
풀이 확인
화살표 문법 변환, Promise 등 필요한 ECMAScript API 보완, fetch의 별도 Web API 보완을 각각 판단합니다. 타입 표기 제거와 타입 검사는 별도이므로 독립된 타입 검사도 필요합니다. 새 정책으로 다시 만든 산출물을 실제 대상 환경에서 시험해야 합니다. 이전 번들 성공은 새 설정의 결과를 확인한 것이 아닙니다.
Babel과 Polyfill의 관계 요약
| 역할 | Babel (트랜스파일러) | Polyfill (런타임 주입) |
|---|---|---|
| 대상 | 최신 문법(Syntax) | 최신 기능/API(Feature/API) |
| 예시 | 화살표 함수, const, let, class, JSX | Promise, fetch, Array.prototype.includes, Map |
| 방식 | 코드 자체를 변환하여 구형 문법으로 변경 | 누락된 기능을 구현한 코드를 런타임에 추가 |
| 결과 | 문법 오류 해결 | ReferenceError, TypeError 같은 런타임 오류 해결 |
함께 사용하는 이유:
대부분의 모던 웹 프로젝트는 최신 JavaScript 문법을 사용하고 새로운 API도 활용하므로, Babel과 Polyfill을 함께 사용하여 정해 둔 대상 브라우저의 문법과 API 지원 범위를 맞춥니다. fetch 같은 Web API에는 별도 보완이 필요할 수 있으며, 프레임워크의 지원 환경과 실제 브라우저 테스트도 확인해야 합니다.
babel-loader와 @babel/preset-env의 useBuiltIns: "usage" 옵션이 이 둘을 효율적으로 통합하여 관리하는 가장 일반적이고 권장되는 방식입니다.
호환성 설정은 한 번 정하고 끝나는 값이 아니라, 지원 브라우저와 번들 크기 사이에서 계속 조정하는 기준입니다.
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 보완 범위를 함께 조정해야 번들 크기와 호환성을 균형 있게 맞출 수 있습니다.