$ yh.log
[스터디] npm deep dive - 5장 트랜스파일과 폴리필

[스터디] npm deep dive - 5장 트랜스파일과 폴리필

작성자 : 오예환 | 작성일 : 2026-03-03 | 수정일 : 2026-03-03 | 조회수 :

5장 트랜스파일과 폴리필

  • TC39(Technical Committee 39): ECMA 하위 위원회로, 자바스크립트 표준을 개발하고 유지하며 단계적으로 발전시키고 있음
  • 이에 따라 브라우저도 함께 발전해야 함
  • 새로운 자바스크립트 기능 사용 시 발생하는 호환성 문제는 트랜스파일과 폴리필을 통해 해결할 수 있음
  • 트랜스파일러는 최신 기능이 지원되지 않는 브라우저에서도 안정적으로 동작할 수 있도록 도움

5.1 트랜스파일을 도와주는 도구, 바벨

바벨(Babel)은 최신 자바스크립트 코드를 구형 브라우저와 호환되는 코드로 변환해주는 트랜스파일러

5.1.1 바벨의 필요성

배경

  • 2015년 ES6에서 많은 새로운 기능과 문법이 도입되어 개발이 더욱 강력하고 편리해짐
  • 하지만 인터넷 익스플로러 11, 사파리 9 같은 구형 브라우저에서는 최신 기능을 지원하지 않음
  • 많은 사용자가 최신 브라우저를 사용하지 않거나, 기업 환경에서 특정 버전의 브라우저 호환성을 유지해야 하는 경우 ES6 기능을 사용할 수 없음

트랜스파일러란?

  • 한 버전의 언어를 다른 버전으로 변환해주는 도구
  • 자바스크립트에서는 최신 문법을 구형 브라우저에서 동작하도록 코드를 변환해준다는 의미

예시

// ES6
const sum = (a, b) => a + b;
 
// ES5로 트랜스파일
var sum = function sum(a, b) {
  return a + b;
};

ES6의 모든 명세가 트랜스파일 가능한 것은 아님 (변환이 불가능한 경우도 있음)

바벨의 역사

  • 바벨 등장 전에는 Traceur, es6-shim 등이 있었음
  • 2014년 세바스찬 멕킨지가 6to5라는 이름으로 처음 개발
  • 이후 이름을 Babel로 변경하고, ES5 트랜스파일을 넘어 다양한 자바스크립트 버전과 실험적 기능을 지원하는 다목적 트랜스파일러로 발전

바벨의 특징

  1. 플러그인 시스템: 유연한 확장 가능
  2. 광범위한 문법 지원: ES6을 넘어 다양한 자바스크립트 문법 지원
  3. 프리셋 제공: 여러 플러그인을 쉽게 설정 가능
    • @babel/preset-env
    • @babel/preset-react
    • @babel/preset-typescript
    • @babel/preset-flow
  4. 활발한 커뮤니티: 지속적인 업데이트와 지원

지금도 리액트 프로젝트에서 바벨이 필수일까?

상황추천 도구
구형 브라우저 지원 필요, 안정성 중시바벨 (오랜 시간 검증됨)
모던 브라우저만 지원, 빌드 속도/간단한 설정 중시Vite, esbuild

5.1.2 바벨의 동작 방식

바벨은 **추상 구문 트리(AST)**를 기반으로 동작하며, 변환 과정에 필요한 기능들을 독립적인 패키지로 관리

5.1.2.1 추상 구문 트리 (AST)

정의

  • 소스코드의 구조를 트리 형태로 표현한 자료구조
  • 컴파일러와 인터프리터가 소스코드를 분석하고 변환하는 데 사용하는 핵심 개념

구성요소

  • 노드(Node)
  • 자식 노드(Child Node)
  • 최상위 노드(Root Node)

AST Explorer에서 추상 구문 트리를 쉽게 확인해볼 수 있음

AST를 사용하는 대표적인 예시

도구AST 활용 방식
ES Module파싱 단계에서 코드를 AST로 변환하여 문법 검증 및 import문 분석으로 모듈 의존성 파악
ESLintAST를 사용해 코드 린팅 수행. 각 노드를 탐색하며 린팅 규칙 적용 후 문제 위치와 메시지를 포함한 보고서 생성
Babelacorn 파서 기반의 Babylon 파서를 사용하여 실험적 기능까지 지원 (현재 @babel/parser)

자바스크립트 AST 파서

자바스크립트 코드를 파싱해서 AST 자료구조로 변환하는 도구:

  • acorn
  • esprima
  • espree
  • @babel/parser

5.1.2.2 바벨이 코드를 변환하는 과정

바벨의 코드 변환은 3단계로 진행됨:

소스코드 → [파싱] → AST → [변환] → 수정된 AST → [출력] → 변환된 코드
단계설명사용 패키지
1. 파싱소스코드를 읽어 AST로 변환. 최신 ECMAScript, JSX, Flow, TypeScript 지원@babel/parser
2. 변환AST를 탐색하며 변환이 필요한 노드를 찾아 변경. 깊이 우선 방식으로 탐색@babel/traverse
3. 출력수정된 AST를 다시 코드로 변환하여 최종 출력@babel/generator

변환 단계의 플러그인 예시

  • @babel/plugin-transform-block-scoping: const/letvar
  • @babel/plugin-transform-arrow-functions: 화살표 함수 → 일반 함수

@babel/core

파싱, 변환, 출력의 세 단계를 모두 포함하는 바벨의 핵심 패키지

  • 위에서 설명한 @babel/parser, @babel/traverse, @babel/generator를 의존성으로 포함
  • 파싱부터 출력까지 모든 기능을 수행할 수 있음
  • 바벨 package.json 참고

transform API 사용 예시

transform API는 파싱 → 플러그인/프리셋 적용 → 코드 변환 → 출력까지 한번에 실행할 수 있는 유용한 API

import { transform } from "@babel/core";
 
const code = `
const sum = (a, b) => a + b;
`;
 
const output = transform(code, {
  presets: ["@babel/preset-env"],
});
 
console.log(output.code);

패키지 분리의 장점

바벨이 내부 동작을 여러 패키지로 나누어 제공하는 이유:

  • 모듈화와 유연성: 필요한 기능만 선택적으로 사용 가능
  • 유지보수 용이: 각 패키지를 독립적으로 업데이트 가능

5.1.3 바벨 사용해보기

5.1.3.1 바벨 구성 파일

바벨로 코드를 트랜스파일하려면 먼저 구성 파일이 필요하다.

구성 파일이란?

  • 바벨의 변환 작업을 정의하는 모든 옵션이 포함된 파일
  • transform API 사용 시 이 파일을 읽어 코드 변환 방법을 결정

구성 파일 종류

  • babel.config.* (js, json, cjs, mjs)
  • .babelrc
  • package.jsonbabel 필드

주요 옵션

1. presets

프리셋을 설정. 배열 형태로 여러 프리셋 지정 가능

{
  "presets": ["@babel/preset-env", "@babel/preset-react"]
}

2. plugins

프리셋에서 제공하지 않는 개별 기능이나 실험적 기능을 추가할 때 사용

{
  "presets": ["@babel/preset-env", "@babel/preset-react"],
  "plugins": ["@babel/plugin-transform-runtime", "@babel/plugin-proposal-class-properties"]
}
플러그인설명
@babel/plugin-transform-runtime바벨의 런타임 헬퍼를 재사용하여 코드 중복 방지
@babel/plugin-proposal-class-propertiesES6 표준에 없는 클래스 속성 문법 지원

3. 기타 옵션

옵션설명
env환경별(development, production 등) 설정을 다르게 적용
ignore특정 파일/디렉터리를 트랜스파일에서 제외
only트랜스파일할 파일/디렉터리를 명시적으로 지정
exclude특정 플러그인/프리셋에 대해서만 제외할 파일 지정
include특정 플러그인/프리셋에 대해서만 포함할 파일 지정
sourceMaps소스맵 생성 여부 (true, false, "inline" 등)
compact출력 코드 길이를 줄여 파일 크기 축소 (가독성 저하)
minified트랜스파일된 코드를 축소(minify)할지 여부
retainLines소스코드의 줄 번호를 유지하여 디버깅 용이하게 함
extends다른 바벨 구성 파일을 확장하여 사용
overrides특정 파일/디렉터리에 대해 별도의 바벨 구성 적용

5.1.3.2 단독으로 사용하기

바벨을 단독으로 사용할 때는 CLI 패키지인 @babel/cli를 사용하면 편리하게 코드를 변환할 수 있다.

npm install --save-dev @babel/cli @babel/core

트랜스파일의 한계

트랜스파일은 ES6 명세를 ES5 명세의 조합으로 코드를 대체하는 방식으로 동작한다. 하지만 모든 기능이 변환 가능한 것은 아니다.

구분기능변환 방식
변환 가능클래스함수 기반 프로토타입 패턴
화살표 함수일반 함수 표현식
템플릿 리터럴문자열 연결
구조 분해 할당일반 변수 할당
스프레드 연산자Object.assign 또는 배열 메서드
옵셔널 체이닝삼항 연산자
변환 어려움Promise 객체폴리필 필요
Map, Set, WeakMap, WeakSet폴리필 필요
Array.prototype.includes 등 새 메서드폴리필 필요
async/await폴리필 필요

변환이 어려운 경우, 트랜스파일 외에 폴리필이라는 다른 기능이 필요하다.


5.1.3.3 번들러와 함께 사용하기

바벨을 단독으로 사용할 때는 다음과 같은 제약이 있다.

1. 모듈 시스템 변환 문제

  • 브라우저는 Node.js의 require()module.exports를 지원하지 않음
  • @babel/preset-env에서 targets.esmodules: false 설정 시 ES6 모듈이 CommonJS로 변환됨
  • 브라우저에서 require is not defined 오류 발생 가능
  • 반대로 설정하지 않으면 import/export를 변환하지 못해 구형 브라우저 지원 불가

2. 최적화 문제

  • 코드 분할 불가: 바벨은 단독으로 코드 분할을 지원하지 않아 초기 로드 시간이 길어질 수 있음
  • 다른 파일 형식 미지원: 바벨은 오직 자바스크립트만 변환하므로 CSS, HTML, 이미지 등은 관리할 수 없음

해결책: 번들러와 함께 사용

이러한 문제들로 바벨을 단독으로 사용하기보다 **웹팩(Webpack)**이나 롤업(Rollup) 같은 모듈 번들러와 함께 사용한다.

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader", // 바벨을 웹팩 로더로 사용
        },
      },
    ],
  },
};

5.2 폴리필을 도와주는 도구, core-js

폴리필이 필요한 이유

Promise를 예로 들면, 다음과 같은 인터페이스를 모두 구현해야 한다.

class Promise {
  constructor(executor: (resolve: Function, reject: Function) => void): Promise;
  then(onFulfilled: Function, onRejected: Function): Promise;
  catch(onRejected: Function): Promise;
  finally(onFinally: Function): Promise;
  static all(iterable: Iterable): Promise;
  static any(promises: Iterable): Promise;
  static race(iterable: Iterable): Promise;
  static reject(r: any): Promise;
  static resolve(x: any): Promise;
  static withResolvers(): { promise: Promise; resolve: Function; reject: Function };
}

Promise의 주요 특징

메서드/특징설명
생성자 함수executor 콜백을 받아 비동기 작업 실행. resolve/reject로 상태 결정
then이행(fulfilled) 시 호출될 콜백 등록. 새 Promise 반환
catch거부(rejected) 시 호출될 콜백 등록. then(null, onRejected)의 축약
finally이행/거부 여부와 관계없이 항상 실행되는 콜백 등록
체이닝then/catch/finally가 Promise를 반환하여 연속 호출 가능
Promise.all모든 Promise가 이행되면 이행, 하나라도 거부되면 즉시 거부
Promise.allSettled모든 Promise가 완료(이행 또는 거부)될 때까지 대기
Promise.race가장 먼저 완료되는 Promise의 결과를 반환
Promise.resolve주어진 값을 이행 상태의 Promise로 감싸서 반환
Promise.reject주어진 이유를 거부 상태의 Promise로 감싸서 반환

이렇게 많은 특징을 갖는 Promise는 낮은 ES 버전의 문법만으로는 완벽히 대체하기 어렵다. 따라서 Promise의 모든 인터페이스를 전역에 새롭게 정의해야 한다.

폴리필 구현 예시

if (!window.Promise) {
  window.Promise = function (executor) {
    var PENDING = "pending";
    var FULFILLED = "fulfilled";
    var REJECTED = "rejected";
 
    var state = PENDING;
    var value = undefined;
    var handlers = [];
 
    function resolve(result) {
      if (state !== PENDING) return;
      state = FULFILLED;
      value = result;
      handlers.forEach(handle);
    }
 
    function reject(error) {
      if (state !== PENDING) return;
      state = REJECTED;
      value = error;
      handlers.forEach(handle);
    }
 
    function handle(handler) {
      if (state === PENDING) {
        handlers.push(handler);
      } else if (state === FULFILLED && typeof handler.onFulfilled === "function") {
        handler.onFulfilled(value);
      } else if (state === REJECTED && typeof handler.onRejected === "function") {
        handler.onRejected(value);
      }
    }
 
    this.then = function (onFulfilled, onRejected) {
      return new Promise(function (resolve, reject) {
        handle({
          onFulfilled: function (value) {
            try {
              resolve(onFulfilled ? onFulfilled(value) : value);
            } catch (e) {
              reject(e);
            }
          },
          onRejected: function (error) {
            try {
              resolve(onRejected ? onRejected(error) : error);
            } catch (e) {
              reject(e);
            }
          },
        });
      });
    };
 
    executor(resolve, reject);
  };
}

폴리필(Polyfill): 트랜스파일만으로 해결되지 않는 기능을 지원하기 위해, 동일한 이름으로 구형 브라우저에서도 작동하도록 전역에 정의되는 메서드나 객체

폴리필을 제공하는 대표적인 라이브러리가 core-js이며, 바벨은 이를 프리셋과 플러그인에서 설정할 수 있게 지원한다.


5.2.1 core-js란 무엇인가?

자바스크립트 폴리필 라이브러리로, 최신 ECMAScript 표준과 아직 표준으로 채택되지 않은 제안 기능까지 지원

특징

특징설명
폴리필 제공Promise, Symbol, Map, Set 등 다양한 기능의 폴리필 제공
모듈화필요한 폴리필만 선택적으로 가져올 수 있음
제안 단계 지원TC39 제안 단계(Stage 0~4)의 실험적 기능도 지원

폴리필로 해결할 수 없는 기능

  • Reflect.construct
  • 꼬리 호출 최적화 (Tail Call Optimization)
  • 일부 심볼 기능
  • 클래스의 비공개 필드와 메서드 (#privateField)

이러한 기능은 자바스크립트 엔진 수준에서 깊은 통합과 최적화가 필요하기 때문에 폴리필로 구현하기 어렵다.

core-js 임포트 방법

// 방법 1: 모든 안정적인 폴리필 임포트 (어떤 폴리필이 필요한지 모를 때)
import "core-js/stable";
 
// 방법 2: 필요한 폴리필만 선택적으로 임포트
import "core-js/features/promise";
import "core-js/features/array/includes";

5.2.2 바벨과 core-js

일반적으로 바벨의 트랜스파일 기능과 함께 사용한다. 바벨은 core-js를 의존성으로 포함해서 프리셋과 플러그인에서 폴리필 기능을 설정할 수 있다.

바벨을 통해 core-js를 사용하면 개발자가 직접 어떤 폴리필이 필요한지 결정하지 않아도 되어 훨씬 편리하다.

5.2.2.1 @babel/preset-env에 core-js 설정하기

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": { "browsers": ["> 1%", "last 2 versions"] },
        "useBuiltIns": "usage",
        "corejs": 3
      }
    ]
  ]
}

주요 설정 옵션

옵션설명
corejs사용할 core-js 버전 지정 (예: 3 또는 3.25). 정확한 버전 지정 권장
targets.browsers지원할 브라우저 범위 지정. 이 범위에 맞춰 필요한 폴리필만 포함됨
useBuiltIns폴리필 삽입 방식 설정

useBuiltIns 옵션 상세

설명
false폴리필을 자동으로 추가하지 않음 (기본값)
"entry"엔트리 파일의 import "core-js" 를 타겟에 맞게 필요한 폴리필로 대체
"usage"각 파일에서 실제로 사용하는 기능의 폴리필만 자동 삽입 (권장)

5.2.2.2 런타임에 core-js 폴리필 로드하기

core-js를 직접 임포트하거나 프리셋에서 설정하는 방식이 바람직하지 않은 경우가 있다.

문제점

  1. 큰 번들 크기: 사용하지 않는 폴리필까지 포함될 수 있음
  2. 전역 네임스페이스 오염: Array.prototype.includes 등 전역 객체를 수정함

해결책: @babel/plugin-transform-runtime

전역 오염 없이 core-js를 사용하며, 헬퍼 함수를 모듈로 가져와 사용할 수 있다.

npm install --save-dev @babel/plugin-transform-runtime
npm install --save @babel/runtime-corejs3
{
  "plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "corejs": 3
      }
    ]
  ]
}

두 방식 비교

특징@babel/preset-env + corejs@babel/plugin-transform-runtime
전역 오염O (전역 객체 수정)X (모듈로 가져옴)
번들 크기크기가 클 수 있음필요한 것만 포함하여 작음
설정 난이도쉬움약간 복잡
의존성core-js@babel/runtime-corejs3
사용 권장일반 애플리케이션라이브러리 개발
  • 자바스크립트의 낮은 ES버전의 기능만 지원하는 브라우저에서 효율적으로 지원하기 위해 트랜스파일 뿐만 아니라 폴리필도 필요하다
  • 대표적인 폴리필 패키지는 core-js이고 바벨과 함께 사용하는 방법을 알아보았다.
  • 바벨을 사용하면 ECMAScript 호환성을 트랜스파일과 폴리필로 효율적으로 관리할 수 있다.

5.3 최선의 폴리필과 트랜스파일은 무엇일까?

용어설명
폴리필최신 웹 기술을 지원하지 않는 브라우저에서 해당 기능을 사용할 수 있도록 해주는 코드
트랜스파일최신 자바스크립트 코드를 구형 브라우저에서도 동작할 수 있게 변환하는 것

어떤 특징이 폴리필과 트랜스파일을 설정하는데 영향을 미칠까?

5.3.1 지원 환경 명시하기

  • 주요한 원인은 지원해야하는 브라우저 범위와 Node.js버전 범위
  • 프로젝트가 목표로하는 브라우저를 명확히 하면 불필요한 폴리필과 트랜스파일을 줄일수 있어 번들 크기를 최소화할수있다.
  • 브라우저 범위를 명시하는 방법으로 browserslist에 대해알아보자

5.3.1.1 browserslist

다양한 프런트엔드 도구들이 공통된 브라우저 및 Node.js 버전 설정을 공유할 수 있게 해주는 도구.

  • caniuse-lite 데이터를 사용해 브라우저 지원 정보를 파악
  • Babel, ESLint, Autoprefixer, stylelint, webpack, parcel, rollup 등에서 활용
  • 프로젝트 전체에서 일관된 브라우저 호환성 유지
설정 방법

package.json에서 "browserslist" 필드를 통해서 설정하거나 .browserslistrc 라는 별도의 구성 파일을 생성해서 지원 범위 관리

{
  "name": "project",
  "browserslist": ["defaults and fully supports es6-module", "maintained node versions"]
}
# .browserslistrc
defaults and fully supports es6-module
maintained node versions

쿼리 종류

기준쿼리 예시설명
시장점유율> 5%, >= 5%전세계 사용 통계 기준
> 5% in US특정 국가 기준
> 5% in alt-AS특정 대륙 기준
최신 버전last 2 versions모든 브라우저의 최신 2개 버전
last 2 Chrome versions특정 브라우저의 최신 2개 버전
버전 지정Chrome > 100, ie 11특정 브라우저의 특정 버전
기능 지원supports es6-moduleES6 모듈 지원 브라우저
fully supports css-gridCSS Grid 완전 지원 브라우저
유지보수dead24개월 이상 업데이트 없는 브라우저
not dead유지보수 중인 브라우저
Node.jsnode 16, current node특정 버전 또는 현재 LTS
maintained node versions유지보수 중인 모든 버전

기본 설정 (defaults)

> 0.5%
last 2 versions
Firefox ESR
not dead

쿼리 조합

여러 쿼리를 조합하면 더 정확한 지원 범위 설정 가능

연산자의미예시
or, ,합집합 (하나라도 만족)> 1%, last 2 versions
and교집합 (모두 만족)> 0.5% and last 2 versions
not제외not ie 11, not dead

예시: ES5를 지원하는 브라우저 타겟팅

{
  "name": "ex",
  "browserslist": [">= 0.5%", "last 2 versions", "Firefox ESR", "not dead", "ie 11"]
}
쿼리설명
>= 0.5%시장 점유율 0.5% 이상인 브라우저
last 2 versions각 브라우저의 최신 2개 버전
Firefox ESR파이어폭스 확장 지원 릴리스
not dead유지보수 중인 브라우저만
ie 11IE 11 포함 (ES5 지원 보장)

참고: browsersl.ist에서 쿼리 결과를 미리 확인할 수 있다.

데이터 업데이트 (update-browserslist-db)

browserslist 사용 중 다음과 같은 경고가 나타날 수 있다.

Browserslist: caniuse-lite is outdated.
Please run: npx browserslist@latest --update-db

이 경고는 caniuse-lite의 브라우저 사용 통계나 버전 정보가 오래되었다는 의미.

정기적인 업데이트를 통해 최신 브라우저 정보를 반영하면, 프로젝트에서 실제로 필요한 트랜스파일과 폴리필만 적용할 수 있다.

5.3.1.2 core-js-compat

browserslist로 설정한 지원 범위에 알맞은 폴리필 코드만 추가하는 패키지. core-js 라이브러리와 함께 사용된다.

5.4 바벨과 core-js의 대안

5.4.1 타입스크립트 컴파일러

트랜스파일은 Babel 대신 TypeScript 컴파일러를 사용할 수 있다.

  • 정적 타입 검사뿐만 아니라 트랜스파일까지 수행
  • tsconfig.jsontarget 필드로 대상 ECMAScript 버전 지정
  • target: "ES5"로 설정하면 ES6+ 코드를 ES5로 변환
{
  "compilerOptions": {
    "target": "ES5"
  }
}

target필드에서 사용 가능한 ECMAScript버전은 TypeScript 5버전을 기준으로 다음과 같다.

target설명
ES31999년 표준. 가장 오래된 버전
ES5대부분의 현대 브라우저에서 널리 지원되며, IE 11과 같은 구형 브라우저와 호환
ES6/ES2015let, const, 화살표 함수, 클래스, 모듈 같은 최신 기능 포함
ES2016 ~ ES2022ES6에서 추가 기능이 더해진 확장 버전. 특정 연도로 설정하면 그 이후 버전의 기능 사용 불가
ESNext최신 ECMAScript 제안 기능 대상. TC39의 Stage 3 제안 기능까지 포함

⚠️ target을 지정하지 않으면 불필요하게 구형 브라우저까지 호환되도록 트랜스파일되어 번들 크기가 커지므로 주의

5.4.1.1 트랜스파일에 영향을 주는 옵션들

module : 타입스크립트 컴파일러가 코드를 변환할 때 사용할 모듈 시스템을 결정하는 필드

module설명
none모듈 시스템 미사용. 전역 스코프에서 동작하는 스크립트 작성 시 사용
preserveimport/export 구문을 변환하지 않고 그대로 유지. 번들러가 모듈 처리 담당 시 사용
node16Node.js 16+ 모듈 시스템. package.json의 type 필드에 따라 ESM/CJS 자동 결정
nodenextNode.js 최신 모듈 시스템. node16과 유사하나 향후 Node.js 변경사항 반영
commonjsCommonJS로 변환. Node.js 환경에서 사용
amdAMD(Asynchronous Module Definition)로 변환. RequireJS 환경
umdUMD(Universal Module Definition)로 변환. 범용 모듈 패턴
systemSystemJS 모듈 로더용으로 변환
es6/es2015ES6 모듈 구문(import/export)으로 출력. 모던 브라우저나 번들러 환경
es2020동적 import()import.meta 지원
es2022top-level await 지원
esnext최신 ECMAScript 모듈 기능 모두 지원. 가장 최신의 모듈 문법 사용 가능

lib : 컴파일할 때 포함할 표준 라이브러리의 목록을 지정하는 필드. 사용할 수 있는 기능과 API의 범위를 정의한다.

환경 라이브러리

설명
DOM브라우저의 DOM API
WebWorker웹워커 API
ScriptHostWSH(Windows Script Host) API
WebGLWebGL API

특정 API만 추가하고 싶다면 ESNext.Promise, DOM.Iterable처럼 세부 API를 지정할 수도 있다.

{
  "compilerOptions": {
    "target": "ES5",
    "lib": ["ESNext", "DOM"]
  }
}

lib는 주로 타입 검사와 관련이 있고, 트랜스파일이나 폴리필과는 직접적으로 관계가 없지만 간접적으로 영향을 미칠 수 있다.

{
  "compilerOptions": {
    "target": "ES5",
    "lib": ["ES2017"]
  }
}

위 설정에서 lib: ES2017으로 Promise와 async/await을 사용할 수 있지만, target: ES5이므로 ES5 문법으로 변환된다.

기능처리 방식
async/awaitES5 코드로 트랜스파일
Promise폴리필 추가 필요

lib는 사용 가능한 기능의 범위를 정의하지만, 트랜스파일과 폴리필 관점에서 추가 작업이 필요할 수 있다.

sourceMap : 컴파일된 자바스크립트 파일에 소스맵을 생성할지 여부를 설정하는 필드.

소스맵은 원본 TypeScript 코드와 트랜스파일된 JavaScript 코드 간의 매핑을 제공하는 객체 형태의 데이터다.

소스맵의 장점

기능설명
디버깅브라우저 개발 도구에서 원본 TypeScript 코드의 위치를 정확히 확인 가능
에러 추적오류 스택 추적에서 JS 위치를 TS 위치로 매핑
원본 코드 연결압축/난독화된 코드에서도 원본 코드 정보 제공

소스맵 파일 구조 (.js.map)

{
  "version": 3,
  "file": "index.js",
  "sourceRoot": "",
  "sources": ["../src/index.tsx"],
  "names": [],
  "mappings": "AAAA;IAAA;..."
}
필드설명
version소스맵 파일의 버전
file트랜스파일된 파일 이름
sources원본 파일 이름의 배열
sourceRoot원본 파일 경로의 기본 디렉터리
mappings트랜스파일된 코드와 원본 코드 간의 매핑 정보 (인코딩된 문자열)

참고: source-map-visualization으로 소스맵을 시각화할 수 있다.

환경별 설정

소스맵은 디버깅에 유용하지만, 프로덕션에서는 번들 크기를 늘리므로 환경별로 다르게 설정해야 한다.

{
  "scripts": {
    "build:dev": "tsc --sourceMap true",
    "build:prod": "tsc --sourceMap false"
  }
}

jsx : React처럼 JSX 문법을 사용하는 프로젝트에서 TypeScript 컴파일러가 JSX를 올바르게 트랜스파일하도록 설정하는 필드.

jsx설명React import
preserveJSX 구문을 변환하지 않고 그대로 유지. Babel 등 별도 도구로 최종 변환-
reactReact.createElement() 호출로 변환. React 16 이하 호환필수
react-jsxreact/jsx-runtime 사용. React 17+ 새로운 변환 방식불필요
react-jsxdevreact-jsx와 유사하나 개발 환경용 디버깅 정보 포함불필요
react-nativeReact Native의 React.createElement 형태로 변환필수

tslib : TypeScript 런타임 라이브러리. 중복되는 헬퍼 함수들을 외부 라이브러리로 추출해서 코드 중복을 줄이고 번들 크기를 최적화한다.

Babel의 @babel/plugin-transform-runtime과 유사한 역할을 수행하지만 차이점이 있다.

tslib vs @babel/plugin-transform-runtime

비교 항목tslib@babel/plugin-transform-runtime
폴리필 처리폴리필 기능 없음. 별도로 core-js 등 추가 필요core-js와 연동하여 폴리필 자동 주입 가능
유연성TypeScript 전용. 설정이 단순함다양한 플러그인과 프리셋으로 세밀한 커스터마이징 가능
사용 환경TypeScript 프로젝트 전용JavaScript/TypeScript 모두 사용 가능

이러한 차이점을 고려하여 프로젝트에 맞는 폴리필 관리 전략을 수립해야 한다.

5.4.1.3 타입스크립트 컴파일러와 바벨 비교

비교 항목TypeScript 컴파일러Babel
폴리필내장 플러그인 없음. 별도 설정 필요@babel/preset-env로 자동 폴리필
헬퍼 함수tslib 패키지 제공@babel/plugin-transform-runtime
플러그인 생태계제한적풍부한 플러그인으로 다양한 커스터마이징 가능
컴파일 속도타입 검사로 인해 대규모 프로젝트에서 상대적으로 느림타입 검사 없이 빠른 트랜스파일

5.4.2 SWC (Speedy Web Compiler)

Rust로 개발된 고성능 트랜스파일러. Babel과 비교해서 상당히 빠른 속도를 자랑하며, Rust의 메모리 안전성과 뛰어난 성능 덕분에 빌드 속도가 중요한 대규모 프로젝트에서 특히 유용하다.

Next.js, Vite, Parcel, Deno 등에서 SWC를 사용해 TypeScript를 빠르게 트랜스파일한다.

5.4.2.1 SWC의 특징

특징설명
Babel 호환성유사한 설정 방식으로 기존 Babel 설정을 쉽게 전환 가능. 사용자 정의 플러그인 시스템도 지원
트리 셰이킹사용되지 않는 코드를 제거해 최종 번들 크기 최적화
TypeScript 통합타입 검사 없이 빠르게 트랜스파일 가능
공식 JSON 스키마IDE에서 자동완성, 유효성 검사 및 정적 분석 지원
폴리필 지원core-js 내장 가능, browserslist 통합, 다양한 모듈 시스템 및 압축 지원

5.4.2.2 SWC로 트랜스파일하기

설치
npm install -D @swc/cli @swc/core
설정 파일 (.swcrc)

jsc : JavaScript Compiler 옵션을 정의하는 핵심 설정

옵션설명
parser파서 설정. syntax: "typescript" 또는 "ecmascript", JSX/TSX 지원 여부 등
target트랜스파일 대상 ECMAScript 버전. "es5", "es2015" ~ "es2022", "esnext"
transform코드 변환 옵션. React JSX 변환, decorator, const enum 등 설정
minify코드 압축 활성화 여부. true로 설정 시 minification 수행
keepClassNames클래스 이름 유지 여부. 디버깅이나 리플렉션에 유용

module : 출력 모듈 시스템 설정

설명
es6ES6 모듈 (import/export)
commonjsCommonJS 모듈 (require/module.exports)
umdUMD (Universal Module Definition)
amdAMD (Asynchronous Module Definition)
nodenextNode.js 최신 모듈 시스템

env : 폴리필 및 브라우저 타겟 설정 (Babel의 @babel/preset-env와 유사)

옵션설명
targets지원할 브라우저/환경 지정. browserslist 쿼리 또는 객체 형태 (예: { "chrome": "80" })
mode폴리필 주입 방식. "entry" (진입점에 전체 주입), "usage" (사용된 기능만 주입)
coreJs사용할 core-js 버전 지정. "3.30" 형태로 마이너 버전까지 명시 권장

@swc/helpers : Babel의 @babel/runtime과 유사한 헬퍼 함수 라이브러리. 중복되는 헬퍼 코드를 외부 모듈로 분리해 번들 크기를 최적화한다. jsc.externalHelpers: true 설정과 함께 사용.

5.4.2.3 SWC와 바벨, 타입스크립트 컴파일러 비교

비교 항목SWCBabelTypeScript
설정 방식.swcrc (tsconfig.json과 유사).babelrc / babel.config.jstsconfig.json
성능Rust 기반으로 매우 빠름JavaScript 기반, 상대적으로 느림타입 검사로 인해 느릴 수 있음
폴리필env 필드로 core-js 통합@babel/preset-env로 자동 주입별도 설정 필요
플러그인 생태계성장 중 (Rust로 작성 필요)매우 풍부함제한적

SWC의 장점

  • tsconfig.json과 유사한 설정 방식으로 간편함
  • Babel과 거의 완벽한 호환성 + 뛰어난 성능
  • env 필드로 폴리필 기능 지원

SWC의 도전 과제

  • 플러그인 생태계가 Babel만큼 풍부하지 않음 (사용자 정의 플러그인은 Rust로 작성 필요)
  • 일부 도구/프레임워크와 완벽히 호환되지 않을 수 있음
    • 예: MobX의 데코레이터가 완벽하게 동작하지 않았던 사례
    • Babel은 @babel/plugin-proposal-decorators로 안정적 지원

현재 SWC는 jsc.parser.decorators 옵션으로 최신 ECMAScript 표준에 따른 데코레이터를 지원하지만, 완전한 호환은 어려울 수 있음을 유의해야 한다.

@swc/wasm-typescript 패키지 배포 등 SWC는 계속 발전 중이며, 플러그인 생태계도 성장할 잠재력이 충분하다.

5.4.3 es-shims

core-js를 대신 사용할 수 있는 폴리필 프로젝트. ECMAScript Shims (이하 es-shims)

브라우저나 런타임 환경에서 부족한 부분을 보완하기 위해 폴리필을 제공하는 오픈소스 프로젝트로, JavaScript 기능을 하위 호환성을 지원하지 않는 환경에서도 사용할 수 있게 하는 것을 목표로 한다.

5.4.3.1 폴리필을 로드하는 방식

core-js 방식

targetPropertyundefined인지 확인하고, undefined인 경우에만 폴리필을 적용한다.

es-shims 방식

define-properties 유틸리티 라이브러리를 사용해 프로퍼티 추가 여부를 동적으로 결정한다.

// Array.prototype.findLast 폴리필 예시
function () {
  return Array.prototype.findLast !== polyfill;
}

위 함수는 Array.prototype.findLast가 이미 존재하고, 그 메서드가 polyfill과 다를 경우 네이티브 메서드로 판단한다.

두 라이브러리 모두 실행 시점에 브라우저가 해당 기능을 지원하는지 검사하고, 필요할 때만 폴리필을 적용한다.

5.4.3.2 모듈화된 설계

es-shims와 core-js의 가장 큰 차이는 사용 방식이다.

비교 항목es-shimscore-js
구조기능별로 개별 패키지 분리하나의 큰 패키지에 모든 폴리필 포함
설치필요한 기능만 개별 설치전체 설치 후 필요한 것만 import
번들 크기사용하는 기능만큼만 포함트리 셰이킹에 의존
# es-shims: 필요한 폴리필만 개별 설치
npm install array.prototype.findlast
npm install object.fromentries
 
# core-js: 전체 설치 후 선택적 import
npm install core-js

5.4.3.3 레거시 환경 지원에 최적화된 es-shims

es-shims는 안정적이고 검증된 기능의 폴리필에 초점을 맞춘다.

  • ECMAScript 표준 준수와 호환성을 최우선으로 고려
  • 새로운 기능보다는 기존 사양의 안정성 강화에 중점
  • TC39 Stage 4(정식 표준)에 도달한 기능 위주로 지원

5.4.4 Polyfill.io

또 다른 폴리필 솔루션. CDN 기반으로 폴리필을 제공하는 서비스다.

5.4.4.1 폴리필을 로드하는 방식

core-js나 es-shims와 달리, Polyfill.io는 서버 사이드에서 User-Agent를 분석하여 브라우저에 필요한 폴리필만 동적으로 제공한다.

<script src="https://polyfill.io/v3/polyfill.min.js"></script>
비교 항목core-js / es-shimsPolyfill.io
폴리필 결정 시점빌드 타임 또는 런타임요청 시점 (서버에서)
판단 기준코드 내 feature detectionUser-Agent 헤더 분석
번들 포함포함됨외부 CDN에서 로드

5.4.4.2 서빙 방법

쿼리 파라미터로 필요한 기능 지정 가능

<!-- 특정 기능만 요청 -->
<script src="https://polyfill.io/v3/polyfill.min.js?features=Promise,Array.prototype.includes"></script>
 
<!-- 플래그 옵션 -->
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6&flags=gated"></script>
옵션설명
features필요한 폴리필 기능 지정 (쉼표로 구분)
flags=gatedfeature detection 후 필요한 경우에만 적용
flags=always항상 폴리필 적용 (브라우저 지원 여부 무관)
callback폴리필 로드 완료 후 실행할 콜백 함수 지정

주의: 2024년 Polyfill.io 도메인이 악성 코드를 배포하는 사건이 발생했다. 현재는 Cloudflare나 Fastly에서 제공하는 대체 CDN(cdnjs.cloudflare.com/polyfill)을 사용하거나, 자체 호스팅하는 것을 권장한다.