폴리필 📖
`폴리필`은 자바스크립트 표준에서 새로 추가된 기능을 구현하지 않은 브라우저나 환경에서도 해당 기능을 사용할 수 있게 만들어주는 코드입니다.
예를 들어, 최신 브라우저는 최신 표준의 대부분 기능을 지원하지만, 오래된 브라우저는 지원하지 않는 경우가 많습니다. 이때 폴리필을 통해 부족한 기능을 "채워 넣는" 작업을 합니다.
폴리필은 다음 두 가지 방식으로 작동합니다.
새로운 내장 함수 추가
예를 들어, `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은 사용자가 map메서드에 전달한 함수입니다.
- call은 그 함수를 실행시키는 방법 중 하나 (call 메서드는 인자를 배열로 넘기지 않고 요소 하나씩 넘김)
- callback 함수를 실행시키되, this값을 thisArg로 설정하고, 그 뒤에 있는 값들을 callback 함수의 인자로 전달
💡call 메서드를 사용한 이유
call을 사용하면 this값을 지정할 수 있기 때문에 함수 안에서 this를 원하는 객체로 설정할 수 있습니다. 즉, callback 함수가 실행될 때 그 안에서 this는 thisArg가 됩니다.
- 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;
- 원본 배열을 변경하지 않고, 콜백 함수에서 반환된 값들로 구성된 새로운 배열을 반환
전체 코드 흐름 요약
- Array.prototype.map이 없는 경우에만 정의.
- 호출된 객체(this)가 배열인지 확인.
- 전달된 callback이 함수인지 확인.
- 배열을 순회하며 각 요소에 대해 callback 호출.
- 콜백 반환 값을 새로운 배열에 저장.
- 최종적으로 새 배열을 반환.
사용 예시
const numbers = [1, 2, 3, 4];
const squared = numbers.map((num) => num * num);
console.log(squared); // [1, 4, 9, 16]
결론 🧐
폴리필은 자바스크립트에서 새로운 기능을 지원하지 않는 환경에서도 최신 기능을 사용할 수 있도록 도와줍니다. 위 예시들을 보면 폴리필은 단순히 동작을 메꿔주는 코드일 뿐만 아니라, 개발자가 오래된 환경에서도 최신 기술을 사용할 수 있게 해 준다는 점에서 중요한 역할을 합니다.
해석한 내용이 정확하지 않을 수 있으니 조언은 언제나 환영입니다~~!!
출처 🏷️
'공부 > javascript' 카테고리의 다른 글
javascript 심화 7 (클로저) (0) | 2024.08.07 |
---|---|
javascript 심화 6 (이벤트 루프) (0) | 2024.08.02 |
javascript 심화 5 (Promise, async, await) (0) | 2024.07.29 |
javascript 심화 4 (콜백함수) (0) | 2024.07.29 |
javascript 심화 3 (this, 화살표함수, call, apply, bind) (0) | 2024.07.26 |
javascript 심화 2 (호이스팅, var let const 차이점, TDZ) (5) | 2024.07.24 |
javascript 심화 1 (실행 컨텍스트) (0) | 2024.07.24 |
javascript 기본 4 (불변 객체, 얕은 복사, 깊은 복사) (0) | 2024.07.23 |