JavaScript · 3장 코드 품질과 도구
폴리필과 트랜스파일러: 오래된 브라우저 대응
최신 문법과 기능으로 코드를 써도, 사용자의 브라우저가 그걸 모르면 페이지가 깨져요. 새 코드를 옛 브라우저에서도 돌게 해 주는 두 가지 도구와, 어디까지 지원할지 정하는 법을 정리했어요.
01. 새 코드와 옛 브라우저 사이의 두 가지 빈틈
오래된 브라우저가 최신 코드를 못 돌리는 이유는 두 가지예요. 대응 방법도 각각 달라요.
| 빈틈 | 예 | 옛 브라우저에서 | 해결 |
|---|---|---|---|
| 문법을 몰라요 | a ?? b, obj?.name, #private 필드 | 코드를 읽는 단계에서 SyntaxError — 파일 전체가 실행되지 않아요 | 트랜스파일러 |
| 기능이 없어요 | array.at(), structuredClone, Object.groupBy | 그 줄에 닿을 때 TypeError (…is not a function) | 폴리필 |
02. 문법은 트랜스파일러가 바꿔요
트랜스파일러는 코드를 읽어서 같은 뜻의 옛 문법으로 다시 써 주는 도구예요. Babel이 대표적이고, TypeScript 컴파일러나 Vite가 쓰는 esbuild 같은 빌드 도구도 이 일을 해요. 빌드할 때 한 번 바뀌므로 사용자 브라우저에서는 추가로 할 일이 없어요.
예를 들어 ??는 "null이나 undefined면 오른쪽"이라는 뜻이니, 옛 문법으로는 조건 비교로 풀어 쓸 수 있어요. 도구가 실제로 만드는 코드는 조금 더 복잡하지만 뜻은 같아요.
const input = 0;
const modern = input ?? 10; // 새 문법
const old = (input !== null && input !== undefined) ? input : 10; // 옛 문법으로 풀어 쓴 모양
console.log(modern, old); // 0 0
문법은 이렇게 미리 바꿔 둘 수밖에 없어요. 브라우저가 파일을 읽다가 모르는 문법을 만나면 파일 전체가 실행되지 않기 때문이에요(SyntaxError).
03. 없는 기능은 폴리필이 채워요
폴리필(polyfill)은 브라우저에 없는 내장 기능을 자바스크립트로 직접 만들어 끼워 넣는 코드예요. 먼저 그 기능이 있는지 확인하고(기능 감지), 없을 때만 채워요. 아래에서는 오래된 브라우저인 척 at을 일부러 지운 뒤, 간단한 폴리필로 다시 채워 볼게요.
// 오래된 브라우저인 척: at을 지워요
delete Array.prototype.at;
console.log(typeof [].at); // undefined
// 폴리필: 없을 때만 채워요
if (typeof Array.prototype.at !== 'function') {
Array.prototype.at = function (index) {
const i = Math.trunc(index) || 0;
return this[i < 0 ? this.length + i : i];
};
console.log('폴리필을 넣었어요');
}
console.log([10, 20, 30].at(-1)); // 30
원리는 이렇게 단순하지만, 실제 폴리필은 명세의 세세한 동작까지 똑같이 맞춰야 해서 직접 만들지 않아요. 검증된 라이브러리인 core-js를 쓰고, Babel 같은 도구가 지원 대상 브라우저에 필요한 것만 골라 넣게 해요.
04. 어디까지 지원할지 정하기
"모든 브라우저"를 지원하면 코드가 커지고 느려져요. 그래서 지원할 브라우저 목록을 먼저 정하고, 도구가 그 목록에 맞춰 바꾸고 채우게 해요. 이 목록을 적는 표준 같은 방법이 Browserslist예요. Babel · Autoprefixer 같은 여러 도구가 같은 목록을 읽어요.
package.json에 이렇게 적어요.
{
"browserslist": ["baseline widely available"]
}
Browserslist는 Baseline을 그대로 목록으로 쓸 수 있어요(baseline widely available, baseline newly available, baseline 2024 같은 연도). 빌드 도구 Vite도 기본 대상이 'baseline-widely-available'이에요. 지금 버전 기준으로 Chrome · Edge 111, Firefox 114, Safari 16.4 이상이에요. 이 범위 밖의 옛 브라우저까지 지원해야 하면 그때 대상을 넓히고 폴리필을 더해요.
예전에는 페이지에 <script src="https://…/polyfill.js">처럼 다른 회사의 서버에서 폴리필을 받아 오는 방식도 많이 썼어요. 2024년에 널리 쓰이던 polyfill.io 도메인이 다른 손에 넘어가 악성 코드를 내보낸 사건 이후로는, 필요한 폴리필을 내 빌드 결과물에 직접 포함하는 게 원칙이에요.
아주 오래된 브라우저를 지원해야 해요. 아래 각각에 트랜스파일러와 폴리필 중 무엇이 필요할지 골라 보세요.
- (가)
user?.address - (나)
Object.groupBy(items, fn) - (다)
const add = (a, b) => a + b; - (라)
[3, 1, 2].toSorted() - (마)
count ??= 0;
해답 보기
문법인 (가) 옵셔널 체이닝, (다) 화살표 함수, (마) 논리 대입은 트랜스파일러가 옛 문법으로 바꿔요. 내장 기능인 (나) Object.groupBy, (라) toSorted는 폴리필이 채워요. 구분법: 새로운 기호 · 키워드면 문법, 무엇.이름()으로 부르는 함수면 기능이에요.
05. 실전 포인트
- 언제 쓰나요? 대부분은 빌드 도구(Vite 등)의 기본 설정이 문법 변환을 알아서 해요. 사용자 통계를 보고 지원 범위를 Browserslist로 정한 뒤, 그 범위에 없는 기능을 쓸 때만 core-js 폴리필을 더해요.
- 대신 쓰는 것: 막연히 "옛 브라우저도 되게" 모든 폴리필을 넣지 않아요. 지금 많이 쓰는 기능 대부분은 이미 Widely available이라 폴리필이 대부분의 사용자에게 무게만 더해요. 다른 서버에서 폴리필을 받아 오는
<script>는 쓰지 않아요. - 흔한 실수: 트랜스파일러만 설정하고 "옛 브라우저도 된다"고 믿는 것(기능은 폴리필이 따로 필요해요), 반대로 폴리필로 문법까지 해결하려는 것이에요. 지원 범위를 바꿨다면 실제 그 브라우저에서 한 번은 열어 봐요.
정리
- 모르는 문법은 트랜스파일러(Babel · esbuild 등)가 빌드할 때 옛 문법으로 바꿔요.
- 없는 기능은 폴리필(core-js)이 기능 감지 뒤 채워요. 직접 만들지 말고 검증된 라이브러리를 써요.
- 지원 범위는 Browserslist로 정해요.
baseline widely available처럼 Baseline을 쓸 수 있고, Vite의 기본 대상도 Baseline Widely available이에요. - 폴리필은 다른 서버에서 받아 오지 않고 내 빌드에 넣어요.