Polyfill: 정의, 작동 원리 및 API 에뮬레이션 라이브러리

저자: IT Sectr 게시일: 2026-05-19 읽는 시간: 9 분

Polyfill은 네이티브로 구현되지 않은 환경에서 누락된 기능(API, 메서드, 객체)을 에뮬레이션하는 코드입니다. Polyfill을 사용하면 오래된 브라우저와 런타임에서 최신 JavaScript, CSS 또는 Web API 기능을 사용할 수 있습니다. MDN Web Docs에 따르면 폴리필은 점진적 향상과 브라우저 간 호환성을 보장하는 핵심 도구입니다.

핵심 요점

  • Polyfill은 API가 구현되지 않은 실행 환경에서 누락된 API를 소프트웨어적으로 에뮬레이션하는 것입니다
  • core-js는 모든 stage-4 제안을 지원하는 최신 JavaScript용 표준 폴리필 라이브러리입니다
  • Polyfill.io는 사용자의 브라우저에 대해서만 동적으로 폴리필을 제공하는 서비스입니다
  • 트랜스파일레이션 vs 폴리필: 트랜스파일레이션은 구문을 변환하고(arrow function → function), 폴리필은 새 메서드를 추가합니다(Array.includes, Promise)
  • 기능 감지(Feature detection)는 충돌을 방지하기 위해 폴리필을 로드하기 전에 네이티브 구현 여부를 확인합니다

Polyfill이란?

Polyfill은 실행 환경이 네이티브로 지원하지 않는 기능을 구현하는 코드 조각(일반적으로 JavaScript)입니다. 이 용어는 2009년 Remy Sharp가 언어 유희로 만들어냈습니다. Polyfill은 벽의 균열을 메우는 퍼티인 Polyfilla와 유사합니다. Polyfill은 표준과 특정 브라우저 또는 런타임에서의 지원 간의 격차를 메웁니다.

Polyfill은 기존 코드를 수정하지 않고 실행 환경을 확장합니다. 브라우저가 Array.prototype.includes를 지원하지 않는 경우, 폴리필은 기본 코드가 실행되기 전에 이 메서드를 Array 프로토타입에 추가합니다. 폴리필은 새로운 전역 객체(Promise, Map, Set, Symbol), 정적 메서드(Array.from, Object.assign) 및 프로토타입 메서드를 에뮬레이션할 수 있습니다.

기능 감지(Feature detection)는 폴리필을 설치하기 전에 필수 메커니즘입니다. user-agent(어떤 브라우저인지)를 확인하는 대신 메서드의 존재를 확인해야 합니다: if (!Array.prototype.includes) { Array.prototype.includes = ... }. 이렇게 하면 네이티브 구현이 이미 존재하는 경우 폴리필이 이를 덮어쓰지 않습니다. Google Analytics 및 기타 서비스는 분석을 위해 API 지원 데이터를 수집합니다.

폴리필의 등장

최초의 폴리필은 Internet Explorer 6–8 시대(2005–2009)에 등장했으며, 개발자들이 W3C 표준과 브라우저 구현 간의 격차를 발견했습니다. 이 용어는 Remy Sharp가 2009년 BarCamp London에서 도입했습니다. 최초의 대규모 폴리필은 html5shiv(2009)로, Internet Explorer에서 HTML5 태그(<section>, <article>, <nav>)에 대한 지원을 추가하는 라이브러리입니다.

ES6(2015)과 연간 ECMAScript 업데이트 주기의 도래와 함께 필요한 폴리필의 수가 증가했습니다. 매년 표준에 새로운 메서드(Array.includes, String.padStart, Object.fromEntries, Promise.allSettled)가 추가되지만 오래된 브라우저에서는 지원되지 않습니다. 2014년 es6-shim으로 시작된 core-js는 범용 솔루션이 되었습니다. 2026년 기준으로 core-js에는 ES5–ES2025를 위한 5,000개 이상의 폴리필 모듈이 포함되어 있습니다.

폴리필 가능한 것과 불가능한 것

카테고리폴리필 가능폴리필 불가능
프로토타입 메서드Array.includes, String.startsWith
전역 객체Promise, Map, Set, Symbol
정적 메서드Object.assign, Array.from
언어 구문Arrow functions, async/await, class
Web APIfetch, IntersectionObserverService Worker(네이티브 지원 필요)

Polyfill vs 트랜스파일레이션: 차이점과 상호 작용

트랜스파일레이션은 새로운 구문을 이전 구문으로 변환합니다(const → var, () => {} → function() {}). Polyfill은 누락된 메서드와 객체를 추가합니다(Promise, Array.includes). 이 두 메커니즘은 서로를 보완합니다. 트랜스파일레이션은 코드를 구문적으로 호환되게 만들고, 폴리필은 API 완전성을 보장합니다. Babel + core-js는 완전한 지원을 위한 표준 조합입니다.

Babel @babel/preset-env의 useBuiltIns 옵션은 대상 브라우저에 따라 필요한 폴리필을 결정합니다. useBuiltIns: "usage"는 코드에서 사용된 API를 분석하고 core-js에서 필요한 폴리필만 가져옵니다. useBuiltIns: "entry"는 단일 core-js/stable 가져오기를 통해 대상 브라우저에 대한 모든 폴리필을 가져옵니다.

예제: Array.prototype.includes 폴리필

js
// 폴리필 존재 확인 및 추가
if (typeof Array.prototype.includes !== "function") {
  Object.defineProperty(Array.prototype, "includes", {
    value: function(searchElement, fromIndex) {
      if (this == null) {
        throw new TypeError("Array.prototype.includes called on null or undefined");
      }
      var arr = Object(this);
      var len = arr.length >>> 0;
      if (len === 0) { return false; }
      var start = fromIndex | 0;
      var k = Math.max(start >= 0 ? start : len + start, 0);

      while (k < len) {
        if (arr[k] === searchElement) { return true; }
        k++;
      }
      return false;
    },
    writable: true,
    configurable: true,
  });
}

// 사용법 — 이제 모든 브라우저에서 안전
const arr = [1, 2, 3, 4, 5];
console.log(arr.includes(3)); // true

Array.prototype.includes용 폴리필은 메서드가 Array 프로토타입에 정의되어 있는지 확인합니다. 정의되어 있지 않은 경우 Object.defineProperty를 통해 writable: true, configurable: true 플래그로 속성을 생성합니다. 구현은 ES2016 사양을 따릅니다: null/undefined 확인, 객체로 변환, 음수 fromIndex 처리. 폴리필을 추가한 후 arr.includes(3) 호출은 Internet Explorer 11을 포함한 모든 브라우저에서 작동합니다.

core-js: 표준 폴리필 라이브러리

core-js는 모든 TC39 stage-4 제안(ECMAScript 표준)을 지원하는 가장 포괄적인 JavaScript 폴리필 라이브러리입니다. core-js에는 Promise, Symbol, Map, Set, WeakMap, WeakSet, Array 메서드, String 메서드, Object 메서드, Number 메서드, Math 메서드, Reflect, globalThis 및 모든 stage-4 제안에 대한 폴리필이 포함되어 있습니다. 현재 버전 core-js 3.38+는 ES5–ES2025를 지원합니다.

core-js는 @babel/preset-env와 useBuiltIns 옵션을 통해 Babel과 통합됩니다. 이 통합이 없으면 개발자가 각 폴리필을 수동으로 가져와야 합니다: import "core-js/stable/array/includes". @babel/preset-env는 .browserslistrc의 대상 브라우저에 따라 필요한 가져오기를 자동으로 추가합니다. 이렇게 하면 번들 크기가 줄어듭니다. 필요한 폴리필만 포함됩니다.

예제: fetch 폴리필

Fetch API는 가장 자주 폴리필되는 Web API 중 하나입니다. 네이티브 fetch 구현은 Chrome 42+(2015), Safari 10.1+(2017), Firefox 39+(2015)에서 사용할 수 있지만 Internet Explorer와 오래된 WebView에는 없습니다. whatwg-fetch 폴리필은 XMLHttpRequest를 통해 fetch를 에뮬레이션합니다. 대안으로 isomorphic-fetch(Node.js 및 브라우저용 폴리필) 또는 폴리필이 필요 없는 범용 axios 라이브러리를 사용할 수 있습니다.

js
// 오래된 브라우저에서만 fetch 폴리필 로드
if (typeof self.fetch !== "function") {
  import("whatwg-fetch").then(module => {
    self.fetch = module.fetch;
    console.log("fetch polyfill loaded");
  });
}

// fetch 사용(폴리필 및 네이티브 API 모두에서 작동)
async function loadData() {
  try {
    const response = await fetch("https://api.example.com/data");
    const json = await response.json();
    return json;
  } catch (error) {
    console.error("Failed to load:", error);
  }
}

동적 import import()를 통한 fetch 폴리필은 최신 브라우저가 불필요한 코드를 로드하지 않도록 보장합니다. 폴리필은 비동기적으로 로드되며 메인 스레드를 차단하지 않습니다. 로드 후 self.fetch는 네이티브 구현을 대체하거나 누락된 구현을 추가합니다. 이것은 점진적 향상 기술입니다. 최신 브라우저는 네이티브 코드만 받고, 오래된 브라우저는 추가 폴리필을 받습니다.

core-js와 Babel 통합

js
// babel.config.js — core-js + preset-env
module.exports = {
  presets: [
    ["@babel/preset-env", {
      useBuiltIns: "usage",
      corejs: {
        version: "3.38",
        proposals: true,
      },
      targets: {
        browsers: ["> 0.5%", "not dead", "not op_mini all"],
      },
    }],
  ],
};
none
# .browserslistrc — 대상 브라우저
> 0.5%
last 2 versions
not dead
not op_mini all
ie >= 11
not ios_saf < 12

useBuiltIns: "usage"는 코드를 분석하고 실제로 사용된 폴리필만 추가합니다. corejs.version은 프로젝트의 core-js 버전을 지정합니다. targets.browsers는 최소 브라우저 수준을 정의합니다. 대상 브라우저가 오래될수록 더 많은 폴리필이 포함됩니다. .browserslistrc는 Babel뿐만 아니라 Autoprefixer, PostCSS 및 Stylelint에서도 일관된 타겟팅을 위해 사용됩니다.

Polyfill.io 및 동적 폴리필 로딩

Polyfill.io는 사용자 브라우저에 필요한 폴리필을 동적으로 결정하고 해당 폴리필만 반환하는 서비스(및 동명의 라이브러리)입니다. Polyfill.io는 User-Agent 헤더를 사용하여 브라우저 버전을 확인하고 최소한의 폴리필 세트를 제공합니다. 이는 범용 폴리필 번들과 비교하여 전송되는 데이터 양을 줄입니다.

Polyfill.io 연결은 기본 애플리케이션 코드 앞에 <script> 태그를 통해 수행됩니다. 서비스는 User-Agent를 분석하고 해당 브라우저에 대한 폴리필만 포함된 JavaScript 파일을 반환합니다. Chrome은 폴리필을 받지 않고, IE 11은 전체 세트를 받습니다. 이것은 성능에 최적화된 접근 방식입니다. 최신 브라우저는 불필요한 코드를 로드하지 않습니다.

Polyfill.io 연결

html
<!-- Polyfill.io: 동적 로딩 -->
<script src="https://cdn.polyfill.io/v3/polyfill.min.js?features=Promise%2CArray.prototype.includes%2CObject.assign%2Cfetch"></script>

<!-- 로컬 Polyfill.io 버전 -->
<script src="/js/polyfill.js"></script>
<script>
  // feature detection for fetch
  if (!self.fetch) {
    loadScript("/js/fetch-polyfill.js");
  }
</script>

URL의 features 매개변수는 Polyfill.io에서 로드할 폴리필을 지정합니다. 가능한 값: 메서드 이름(Array.prototype.includes), 전역 객체(Promise) 또는 플래그(es6, es2016). "default" 플래그는 최신 JavaScript를 위한 기본 세트를 로드합니다. 프로덕션 프로젝트의 경우 자체 CDN에서 Polyfill.io를 호스팅하거나 가용성 제어를 위해 라이브러리의 로컬 버전을 사용하는 것이 좋습니다.

모바일 애플리케이션 및 WebView에서의 폴리필

WebView 모바일 애플리케이션(Android WebView, iOS의 WKWebView)은 폴리필에 특별한 환경입니다. WebView 버전은 OS 버전과 설치된 Chrome System WebView 업데이트(Android) 또는 iOS Safari의 WKWebView에 따라 다릅니다. 오래된 Android(4.4, 5.0)에서 WebView는 Chromium 30–37을 기반으로 하며 fetch, Promise, IntersectionObserver를 지원하지 않습니다.

React Native는 JavaScriptCore(iOS) 또는 Hermes(Android)를 사용합니다. 이러한 엔진은 ES6+를 다르게 구현합니다. iOS의 JavaScriptCore는 대부분의 ES6 기능을 지원하지만 일부 stage-3 제안이 없을 수 있습니다. Hermes(React Native 0.70+에서 기본 사용)는 ES 표준의 제한된 세트를 지원하므로 폴리필이 필수입니다.

WebView 지원 확인

js
// WebView용 feature detection
const polyfills = [];

// Promise
if (typeof Promise === "undefined") {
  polyfills.push("Promise");
}

// Fetch API
if (typeof self.fetch === "undefined") {
  polyfills.push("fetch");
}

// IntersectionObserver(지연 로딩에 필요)
if (typeof IntersectionObserver === "undefined") {
  polyfills.push("IntersectionObserver");
}

// 동적 폴리필 로딩
if (polyfills.length > 0) {
  const script = document.createElement("script");
  script.src = "https://cdn.polyfill.io/v3/polyfill.min.js"
    + "?features=" + polyfills.join(",");
  document.head.appendChild(script);
}

WebView용 기능 감지는 중요한 API(Promise, fetch, IntersectionObserver)의 존재를 확인하고 누락된 것에 대해서만 동적으로 폴리필을 로드합니다. 이를 통해 최신 WebView(Android 12의 Chrome 100+)는 불필요한 코드를 로드하지 않고, 오래된 WebView(Android 5.0)는 필요한 지원을 받을 수 있습니다.

자주 묻는 질문

React Native에 폴리필이 필요한가요?

React Native Hermes에서 일부 ES 메서드(Array.flat, Array.flatMap, globalThis, TextEncoder)에 폴리필이 필요합니다. 프로덕션 빌드에는 core-js 또는 react-native-polyfill-globals를 포함하는 것이 좋습니다. iOS의 JavaScriptCore는 더 많은 기능을 지원하지만 stage-3 제안에 폴리필이 필요할 수도 있습니다.

폴리필이 성능에 영향을 미치나요?

폴리필은 JavaScript 구현이 엔진의 네이티브 C++ 구현보다 느리기 때문에 성능이 1–5% 저하됩니다. 예를 들어 순수 JS의 Promise 폴리필은 V8의 네이티브 Promise보다 느립니다. 그러나 대부분의 애플리케이션에서는 차이가 미미합니다. 중요한 코드의 경우 기능 감지를 통해 네이티브 구현을 확인하는 것이 좋습니다.

폴리필과 트랜스파일레이션의 차이점은?

트랜스파일레이션은 구문을 변환합니다: const → var, 화살표 함수 → function. 폴리필은 새 객체/메서드를 추가합니다: Promise, Array.includes, fetch. 트랜스파일레이션은 빌드 시에 작동하고, 폴리필은 런타임에 로드됩니다. 오래된 환경에서 최신 코드를 완전히 지원하려면 두 메커니즘이 모두 필요합니다.

2026년에 폴리필을 피할 수 있나요?

가능합니다. 대상 사용자가 최신 브라우저(Chrome 90+, Safari 15+, Firefox 90+)만 사용하는 경우입니다. 오래된 기기나 기업 사용자(Internet Explorer 11은 여전히 공공 부문에서 사용됨)를 지원하는 프로젝트의 경우 폴리필이 필수입니다. Google Analytics를 통해 사용자의 브라우저 통계를 분석하세요.

core-js 폴리필 번들의 크기는?

core-js 전체 빌드는 약 85KB(gzip)입니다. Babel에서 useBuiltIns: "usage"를 사용하면 필요한 폴리필만 포함되어 대상 브라우저에 따라 크기가 5–30KB로 줄어듭니다. 최신 브라우저(Chrome 100+)의 경우 폴리필이 전혀 필요하지 않을 수 있습니다.

요약

  • Polyfill은 실행 환경에서 누락된 API를 에뮬레이션하여 오래된 환경에서도 최신 코드가 작동하도록 합니다
  • core-js는 @babel/preset-env를 통해 Babel과 통합된 ES5–ES2025용 표준 폴리필 라이브러리입니다
  • Polyfill.io는 브라우저의 User-Agent를 기반으로 동적으로 폴리필을 로드하는 서비스입니다
  • 트랜스파일레이션 + 폴리필은 포괄적인 솔루션입니다: Babel이 구문을 변환하고 core-js가 누락된 API를 추가합니다
  • 기능 감지는 성능 최적화를 위해 폴리필을 로드하기 전에 네이티브 구현을 확인합니다
  • WebView와 Hermes는 오래된 버전에서 fetch, Promise, IntersectionObserver에 필수 폴리필이 필요합니다

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기