본문 바로가기

공부/javascript

폴리필이란?

반응형

 

폴리필 📖

`폴리필`은 자바스크립트 표준에서 새로 추가된 기능을 구현하지 않은 브라우저나 환경에서도 해당 기능을 사용할 수 있게 만들어주는 코드입니다.

 

예를 들어, 최신 브라우저는 최신 표준의 대부분 기능을 지원하지만, 오래된 브라우저는 지원하지 않는 경우가 많습니다. 이때 폴리필을 통해 부족한 기능을 "채워 넣는" 작업을 합니다.

 

폴리필은 다음 두 가지 방식으로 작동합니다.

새로운 내장 함수 추가

예를 들어, `Array.prototype.includes` 같은 함수가 오래된 브라우저에 없을 경우 이를 직접 구현해 사용할 수 있게 만듭니다.

기존 함수의 동작 수정

브라우저에 이미 존재하는 기능이 최신 표준과 맞지 않다면, 이를 수정해 동작을 맞춥니다.

 

왜 폴리필이 중요한가요? 📖

폴리필은 개발자가 최신 기술로 코드를 작성하되, 모든 사용자가 동일한 경험을 할 수 있도록 보장해줍니다.

 

❗특히, 오래된 환경에서 동작해야 하는 애플리케이션에서는 필수적입니다.

 

현재 우리가 폴리필을 사용하는 방식 📖

현재는 `core-js`나 `polyfill.io` 같은 라이브러리를 주로 사용합니다.

 

이 라이브러리들은 최신 표준 기능의 폴리필을 모아놓은 것으로, 특정 브라우저에서 필요한 폴리필만 자동으로 적용해 줍니다. 예를 들어, 최신 프로젝트에서 Babel과 함께 Core-js를 설정하면 코드가 트랜스파일(transpile)되면서 필요한 폴리필이 자동으로 삽입됩니다.

 

설정 과정

먼저 `babel`과 `core-js`를 설치합니다.

npm install @babel/core @babel/cli @babel/preset-env core-js --save-dev

 

babel 설정 파일 작성 (babel.config.json)

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage", // 필요한 폴리필만 자동으로 삽입
        "corejs": 3 // 최신 버전의 core-js를 사용
      }
    ]
  ]
}

 

`useBuiltins` 옵션을 설정하지 않으면 모든 폴리필을 로드해서 번들 크기가 커질 수 있어서 필요한 폴리필만 로드하도록 설정합니다.

 

코드 작성

`src/index.js` 파일에 코드를 작성합니다.

// ES6+ 코드
const arr = [1, 2, 3];
console.log(arr.includes(2)); // 배열에서 값 포함 여부 확인

const square = (n) => n ** 2;
console.log(square(3)); // ES2020의 제곱 연산자

 

babel을 통해 트랜스파일

아래 명령어를 통해 babel로 코드를 트랜스파일합니다.

npx babel src --out-dir dist

 

`dist/index.js` 결과 코드

"use strict";

require("core-js/modules/es.array.includes.js");
require("core-js/modules/es.object.to-string.js");

var arr = [1, 2, 3];
console.log(arr.includes(2)); // 배열에서 값 포함 여부 확인

var square = function square(n) {
  return Math.pow(n, 2); // 제곱 연산자 변환
};
console.log(square(3));

 

최신 기능(`includes`, `**`)을 분석해서, core-js에서 해딩 기능에 필요한 폴리필을 삽입합니다.

 

 브라우저가 오래된 환경이라도 트랜스파일된 코드와 폴리필 덕분에 문제없이 동작합니다. 그리고 사용된 기능에 필요한 폴리필만 포함되므로 코드 크기가 최소화돼서 효율적입니다.

 

ES5 이전 버전에는 폴리필을 어떻게 적용했는지 📖

과거에는 이렇게 자동으로 폴리필을 추가해주는 도구가 없었습니다.

개발자들이 직접 폴리필을 작성하거나, 인터넷에서 코드를 복사해 프로젝트에 넣어 사용했습니다.

 

예를 들어, ES6에서 추가된 Array.prototype.map을 사용할 수 없는 브라우저에서는 개발자가 이를 직접 구현한 폴리필을 작성해야 했습니다.

폴리필 예시

Array.prototype.map

// ES5 이전 버전에서 map 폴리필 구현
if (!Array.prototype.map) {
  Array.prototype.map = function (callback, thisArg) {
    if (this == null) {
      throw new TypeError("Array.prototype.map called on null or undefined");
    }
    if (typeof callback !== "function") {
      throw new TypeError(callback + " is not a function");
    }
    var O = Object(this);
    var len = O.length >>> 0;
    var result = new Array(len);

    for (var i = 0; i < len; i++) {
      if (i in O) {
        result[i] = callback.call(thisArg, O[i], i, O);
      }
    }

    return result;
  };
}

 

설명

 

Array.prototype.map 존재 여부 확인

if (!Array.prototype.map) {
  Array.prototype.map = function (callback, thisArg) { ... }
}
  • `Array.prototype.map`이 이미 정의된 환경에서는 재정의하지 않게 보호.
  • `Array.prototype.map`이 undefined인 경우에만 새롭게 정의.

 

this가 유효한 객체인지 확인

if (this == null) {
  throw new TypeError("Array.prototype.map called on null or undefined");
}
  • `Array.prototype.map` 메서드가 호출될 때 배열 객체를 가리킵니다. 이 메서드는 `Array.prototype`에 직접 정의되어 있기 때문에 `map` 메서드를 호출한 객체가 배열일 때 `this`는 그 배열을 가리키게 됩니다.
  • this == null은 this가 null 또는 undefined인 경우를 확인합니다. 배열이 아닌 값에서 호출하면 TypeError를 던집니다.

 

💡this가 가리키는 객체

Array.prototype.map 메서드는 배열 객체에 의해 호출됩니다. 예를 들어, [1, 2, 3].map(...)와 같이 호출되면, this는 [1, 2, 3] 배열이 됩니다. 이 배열을 순회하면서 callback을 호출하고 결과를 새로운 배열로 만들어 반환합니다.

 

 

callback이 함수인지 확인

if (typeof callback !== "function") {
  throw new TypeError(callback + " is not a function");
}
  • map 메서드는 반드시 함수(callback)를 인자로 받아야 합니다. 그래서 callback이 아니면 TypeError를 던집니다.

 

객체 래핑 및 배열 길이 추출

var O = Object(this);
var len = O.length >>> 0;
  • Object(this): 원시값을 객체로 래핑해서 처리할 수 있게 this값을 객체로 변환합니다.
  • len = O.length >>> 0: 비트 연산자를 사용해 음수, 소수 값을 안전하게 정수로 변환해 배열 길이를 정확히 다룰 수 있도록 정수로 변환.

객체 래핑 및 배열 길이 추출

var result = new Array(len);
  • new Array(len): 원본 배열과 같은 길이의 빈 배열을 생성합니다.

 

배열 순회 및 콜백 호출

for (var i = 0; i < len; i++) {
  if (i in O) {
    result[i] = callback.call(thisArg, O[i], i, O);
  }
}
  • 원본 배열의 각 요소를 순회하며 콜백 함수를 호출하고, 반환된 값을 결과 배열에 저장합니다.
  • i in O: 현재 인덱스 i에 해당하는 값이 배열에 실제로 존재할 때만 콜백을 호출
  • callback.call(thisArg, O[i], i, O): callback 함수에 세 가지 인자를 전달
    • O[i]: 현재 요소 값.
    • i: 현재 인덱스.
    • O: 원본 배열.
  • thisArg: callback 내부에서 사용할 this 값을 설정

`map` 메서드는 배열을 하나씩 돌아가면서 어떠한 작업을 하고 그 작업의 결과로 새로운 배열을 만들어 반환해 줍니다.

 

`callback.call(thisArg, O[i], i, O)`

  • callback은 사용자가 map메서드에 전달한 함수입니다.
  • call은 그 함수를 실행시키는 방법 중 하나 (call 메서드는 인자를 배열로 넘기지 않고 요소 하나씩 넘김)
  • callback 함수를 실행시키되, this값을 thisArg로 설정하고, 그 뒤에 있는 값들을 callback 함수의 인자로 전달

💡call 메서드를 사용한 이유

call을 사용하면 this값을 지정할 수 있기 때문에 함수 안에서 this를 원하는 객체로 설정할 수 있습니다. 즉, callback 함수가 실행될 때 그 안에서 this는 thisArg가 됩니다.

`O[i], i, O`

  • O[i]는 배열의 i번째 요소입니다. 예를 들어, 배열이 [1, 2, 3]이라면 O[0]은 1, O[1]은 2, O[2]는 3입니다.
  • i: 배열의 인덱스 번호입니다. 즉, 배열에서 각 요소의 순서 번호입니다. 예를 들어, 첫 번째 요소는 i=0, 두 번째 요소는 i=1과 같습니다.
  • O: 전체 배열 자체입니다. 즉, 배열 전체가 callback 함수에 전달됩니다.

`예시로 이해`

const numbers = [1, 2, 3, 4];
const doubled = numbers.map(function (num) {
  return num * 2;
});
console.log(doubled); // [2, 4, 6, 8]

 

여기서 중요한 점

  • callback 함수가 각 요소에 대해 실행될 때 this 값과 배열 요소(예: O[i]), 인덱스(예: i), 전체 배열(예: O)을 callback 함수에 전달한다는 것입니다.

결과 반환

return result;
  • 원본 배열을 변경하지 않고, 콜백 함수에서 반환된 값들로 구성된 새로운 배열을 반환

 

전체 코드 흐름 요약

  1. Array.prototype.map이 없는 경우에만 정의.
  2. 호출된 객체(this)가 배열인지 확인.
  3. 전달된 callback이 함수인지 확인.
  4. 배열을 순회하며 각 요소에 대해 callback 호출.
  5. 콜백 반환 값을 새로운 배열에 저장.
  6. 최종적으로 새 배열을 반환.

사용 예시

const numbers = [1, 2, 3, 4];
const squared = numbers.map((num) => num * num);
console.log(squared); // [1, 4, 9, 16]

 

결론 🧐

폴리필은 자바스크립트에서 새로운 기능을 지원하지 않는 환경에서도 최신 기능을 사용할 수 있도록 도와줍니다. 위 예시들을 보면 폴리필은 단순히 동작을 메꿔주는 코드일 뿐만 아니라, 개발자가 오래된 환경에서도 최신 기술을 사용할 수 있게 해 준다는 점에서 중요한 역할을 합니다.

 

해석한 내용이 정확하지 않을 수 있으니 조언은 언제나 환영입니다~~!!


출처 🏷️

https://ko.javascript.info/

 

반응형