Home
PostsAbout
javascript

바벨과 폴리필

바벨과 폴리필의 차이점?

2025-03-29

서론

자바스크립트는 끊임없이 진화하는 언어입니다. 새로운 제안(proposal)이 정기적으로 등록, 분석되고, 가치가 있다고 판단되는 제안은 https://tc39.github.io/ecma262/에 추가됩니다. 그리고 궁극적으로 명세서(specification)에 등록됩니다.

자바스크립트 엔진을 만드는 각 조직은 나름대로 우선순위를 매겨 명세서 내 어떤 기능을 먼저 구현할지 결정합니다. 명세서에 등록된 기능보다 초안(draft)에 있는 제안을 먼저 구현하기로 결정하는 경우도 있습니다. 구현 난도가 높아서 이런 결정을 내리는 경우도 있지만, 구미를 당기지 않아 이런 결정을 내리기도 합니다.

바벨

명세서에 등록된 지 얼마 안 된 기능을 사용해 코드를 작성하다 보면 특정 엔진에서 우리가 작성한 코드를 지원하지 않는다는 걸 알게 되는 경우가 있습니다. 명세서 내 모든 기능을 모든 엔진이 구현하고 있지 않기 때문이죠.

이럴 때 바벨을 사용할 수 있습니다.

바벨(Babel)은 트랜스파일러(transpiler)로, 모던 자바스크립트 코드를 구 표준을 준수하는 코드로 바꿔줍니다.

바벨의 주요 역할은 다음과 같습니다.

  1. 트랜스파일러 – 바벨은 코드를 재작성해주는 트랜스파일러 프로그램입니다. 바벨은 개발자의 컴퓨터에서 돌아가는데, 이를 실행하면 기존 코드가 구 표준을 준수하는 코드로 변경됩니다. 변경된 코드는 웹사이트 형태로 사용자에게 전달됩니다. 웹팩(webpack)과 같은 모던 프로젝트 빌드 시스템은 코드가 수정될 때마다 자동으로 트랜스파일러를 동작시켜줍니다. 이런 과정이 없으면 개발이 끝난 코드를 한데 통합하는 데 어려움이 있을 수 있습니다.

  2. 폴리필

    명세서엔 새로운 문법이나 기존에 없던 내장 함수에 대한 정의가 추가되곤 합니다. 새로운 문법을 사용해 코드를 작성하면 트랜스파일러는 이를 구 표준을 준수하는 코드로 변경해줍니다. 반면, 새롭게 표준에 추가된 함수는 명세서 내 정의를 읽고 이에 맞게 직접 함수를 구현해야 사용할 수 있습니다. 자바스크립트는 매우 동적인 언어라서 원하기만 하면 어떤 함수라도 스크립트에 추가할 수 있습니다. 물론 기존 함수를 수정하는 것도 가능합니다. 개발자는 스크립트에 새로운 함수를 추가하거나 수정해서 스크립트가 최신 표준을 준수할 수 있게 작업할 수 있습니다.

    이렇게 변경된 표준을 준수할 수 있게 기존 함수의 동작 방식을 수정하거나, 새롭게 구현한 함수의 스크립트를 "폴리필(polyfill)"이라 부릅니다. 폴리필(polyfill)은 말 그대로 구현이 누락된 새로운 기능을 메꿔주는(fill in) 역할을 합니다.

    주목할 만한 폴리필 두 가지는 아래와 같습니다.

    • core js – 다양한 폴리필을 제공합니다. 특정 기능의 폴리필만 사용하는 것도 가능합니다.
    • polyfill.io – 기능이나 사용자의 브라우저에 따라 폴리필 스크립트를 제공해주는 서비스입니다.

모던 자바스크립트를 이용해 스크립트를 작성하려면 트랜스파일러와 폴리필은 필수입니다.

폴리필에 대한 보충 설명

오래된 버전의 브라우저에서는 현재 JavaScript가 당연하게 사용하고 있는 PromiseSet 객체가 없는 경우가 있습니다. 편리한 Array.prototype.at() API는 Chrome 92 이상에서만 지원되기도 합니다.

예를 들어서, 아래와 같은 코드는 최신 브라우저에서는 잘 동작하지만, 오래된 브라우저에서는 실패합니다. 객체나 메서드에 대한 구현이 없기 때문이죠.

[1, 2, 3].at(-1);

Promise.resolve(1);

new Set(1, 2, 3);

이런 문제를 해결하기 위해서는 오래된 브라우저에서 없는 구현을 채워주어야 합니다. 이렇게 구현을 채워주는 스크립트를 Polyfill이라고 합니다. 대부분의 Polyfill은 아래와 같이 이미 브라우저에 포함되어 있는지 체크하고, 없으면 값을 채워주는 형태로 동작합니다.

Array.prototype.at = Array.prototype.at ?? /* Array.prototype.at에 대한 자체 구현 */;

바벨과 폴리필의 차이점

바벨의 역할에 대해 다시 정리해보자면 바벨은 최신 JavaScript 버전의 문법을 이해하지 못하는 오래된 브라우저를 위해, 최신 JavaScript 문법으로 쓰여진 코드를 오래된 브라우저도 이해할 수 있는 문법으로 트랜스파일링하는 툴입니다.

그런데 모든 최신 JavaScript 문법이 트랜스파일링되는 것은 아닙니다. 그렇다면 왜 모든 JavaScript 코드를 트랜스파일링하지 않는 것인지 궁금해집니다.

무엇을 트랜스파일링하는지에 대한 기준

  1. ES5(혹은 그 이전)의 global namespace(window)에 존재하지 않는 것은 트랜스파일링하지 못합니다.

    1. 새로운 객체 (Promise, IntersectionObserver, Set, Map …)
    2. 기존 객체의 새로운 메서드 (Array.prototype.includes, Object.entries …)
    3. 새로운 함수 (fetch)

    바벨은 window.Object, window.Array와 같이 전역 공간에 생성되는 객체가 아니거나, 기존에 존재하지 않는 메서드를 만들어주지 않습니다.

    정리하자면, ES5 환경의 전역 객체에 존재하지 않는 객체, 메서드, 함수는 바벨이 트랜스파일링하지 않고 폴리필이 필요합니다.

  2. 새로운 문법일 경우 트랜스파일링이 가능합니다.

    1. const, let
    2. spread operator
    3. arrow function
    4. class
    5. destructuring

    새로운 객체, 메서드, 함수가 아니라 문법의 경우 바벨이 트랜스파일링 가능합니다.

Polyfill의 문제

폴리필을 작성하면 다양한 브라우저를 지원할 수 있다는 장점이 생기지만 문제도 생깁니다. 바로 불러와야 하는 JavaScript 코드가 많아진다는 점입니다. 실행해야 하는 Polyfill 스크립트가 많아질수록 사용자가 경험하는 웹 서비스의 성능은 나빠집니다.

최신 버전의 브라우저에서는 대부분의 ECMAScript 표준 객체와 메서드가 포함되어 있음에도 불구하고 불필요한 Polyfill 스크립트를 내려받아야 합니다. 이를 해결하기 위한 방법을 정리합니다.

1. @babel/preset-env 사용하기

@babel/preset-env Smart Preset을 사용하면 이미 정의된 브라우저 목록에 따라서 자동으로 필요 없는 폴리필을 제거해줍니다.

module.exports = {
  presets: [
    ['@babel/preset-env', { targets: { ie: 11 } }],
  ],
  /* 그 외의 설정 */
};

@babel/preset-env에 브라우저 지원 범위를 설정하면 폴리필을 안정적으로 포함하면서 스크립트의 크기를 감축할 수 있습니다.

2. User-agent에 따라 동적으로 스크립트 생성하기

위 바벨 설정을 사용하면 포함되는 폴리필 스크립트의 크기를 줄일 수 있지만, 최신 버전의 브라우저에서 불필요한 스크립트를 내려받게 되는 문제는 동일합니다.

이 문제를 해결하는 또 다른 방법은 브라우저의 User-agent에 따라서 동적으로 폴리필 스크립트를 생성하는 것입니다. 예를 들어서, Financial Times에서 관리하고 있는 polyfill.io 서비스에서는 https://polyfill.io/v3/polyfill.min.js 라고 하는 경로로 동적인 Polyfill 스크립트를 제공합니다.

이렇게 User-agent에 따라 동적으로 Polyfill 스크립트를 생성하면 최신 브라우저에서는 아무 Polyfill도 내려주지 않고, 오래된 브라우저에서는 필요한 Polyfill 만 내려줄 수 있게 됩니다.

참고 자료