Chrome 37 키오스크의 폴리필 적용 삽질기
당구장 테이블 옆에는 두 선수의 점수를 기록하는 태블릿이 붙어 있습니다. 대대당구의 키오스크도 여기에 띄우는 화면인데, 매장에 이미 설치된 기기를 써야 해서 개발할 때부터 브라우저 버전을 마음대로 정할 수 없었습니다.

일부 태블릿은 새 크롬을 설치하기도 어려워 웹뷰로 화면을 띄우는 APK를 따로 준비했습니다. 매장에 방문해서 USB로 설치하는 방식이었고, 웹 화면의 지원 목표도 Chrome 37까지 내려갔습니다.
화면을 만드는 데부터 손이 더 갔습니다. flex 요소 사이의 간격은 gap 대신 마진을 쓰고, 정사각형이 필요한 곳은 가로와 세로 크기를 각각 지정했습니다. 평소라면 클래스 하나로 끝날 자리에 구형 기기용 처리가 늘어났습니다.
이런 차이는 적어도 화면을 보면 알 수 있었습니다. 더 곤란한 쪽은 화면이 뜨기도 전에 자바스크립트 실행이 멈추는 문제였습니다.
폴리필을 넣었는데도 실패
자바스크립트 문법을 구형 브라우저에 맞춰 빌드하는 것과 브라우저 API를 채워 넣는 것은 별개의 작업입니다. fetch처럼 실행 환경이 제공해야 하는 기능은 폴리필을 앱보다 먼저 불러오도록 했습니다.
대응하던 웹뷰에는 fetch와 Headers가 존재하면서도 필요한 호출을 처리하지 못하는 경우가 있었습니다. Headers는 HTTP 헤더를 담는 객체인데, 다음처럼 초기값을 넣어 만드는 단계에서 예외가 났습니다.
new window.Headers({ a: "1" })
사용하던 fetch 폴리필은 전역에 fetch가 있으면 설치를 건너뛰었습니다. 파일을 정상적으로 불러왔어도 기존 구현이 남는 구조라, 폴리필을 추가했다는 사실만으로 이 문제가 해결되지는 않았습니다.
여기서는 API의 존재 여부보다 앱이 사용하는 호출을 해보는 편이 맞았습니다. 폴리필을 불러오기 전에 Headers를 생성해 보고, 없거나 예외가 나면 관련 API를 비워 폴리필이 설치되게 했습니다.
var needsPolyfill = false
try {
if (typeof window.Headers !== "function") {
needsPolyfill = true
} else {
new window.Headers({ a: "1" })
}
} catch (e) {
needsPolyfill = true
}
if (needsPolyfill) {
var names = ["fetch", "Headers", "Request", "Response"]
for (var i = 0; i < names.length; i++) {
try {
Object.defineProperty(window, names[i], {
value: undefined,
writable: true,
configurable: true,
})
} catch (e) {
window[names[i]] = undefined
}
}
}
Headers만 교체하지 않고 함께 쓰이는 API 네 개를 비웠습니다. 이 폴리필의 설치 여부를 결정하는 것은 fetch였고, 설치할 때 네 API를 함께 제공했기 때문입니다.
실행 순서도 맞춰야 했습니다. 이 검사가 폴리필 뒤에 있으면 설치를 이미 건너뛴 다음에 기존 API까지 비우게 됩니다. HTML 안에서 먼저 검사하고, 그다음 폴리필, 마지막으로 앱 코드를 불러오도록 순서를 잡았습니다.
이 검사는 fetch의 모든 동작을 검증하지는 않습니다. 당시 막혔던 생성자 호출을 확인하는 검사라서, 이것만 통과했다고 웹뷰 호환성 대응이 끝나는 것은 아니었습니다.
현장에서 오류를 읽는 방법
문제를 고치는 동안에도 현장 기기를 개발 PC처럼 들여다볼 수는 없었습니다. 매장에서는 흰 화면만 보이는데 개발 환경에서는 잘 뜨면, 어느 단계에서 멈췄는지부터 알아내야 했습니다.
앱이 시작되기 전에 HTML의 인라인 스크립트에서 window.onerror를 등록하고, 전달받은 메시지와 파일 위치, 스택을 화면에 표시하도록 했습니다. 앱 화면을 그리다 실패한 경우에도 오류를 확인할 수 있도록 앱 렌더링과 분리했습니다.
흰 화면 사진만 받을 때와 달리 어떤 호출이 실패했는지 확인할 단서가 생겼습니다. 현장에서 오류 화면을 찍어 보내주면 그 내용을 보고 다음 수정 지점을 찾는 식으로 대응했습니다.
이 장치가 모든 실패를 잡는 것은 아닙니다. 핸들러를 등록하기 전의 오류나 처리되지 않은 Promise 거부는 별도로 봐야 하고, 화면에 오류를 그리는 코드 자체도 실행될 수 있어야 합니다. 부팅 중 오류를 확인할 수단 하나를 추가한 셈입니다.
지원 범위를 확인하는 단위
지원하지 않는 API를 찾는 것보다 이미 있는 API를 어디까지 믿어도 되는지 확인하는 일이 더 오래 걸렸습니다. 폴리필을 불러왔다는 것과 실제로 그 구현이 선택됐다는 것도 달랐습니다.
키오스크의 호환성을 점검할 때는 빌드 성공에 더해 앱이 쓰는 호출이 해당 기기에서 되는지, 실패하면 어떤 정보를 받을 수 있는지까지 봐야 했습니다. 매장에 설치하고 돌아온 뒤에는 기기를 바로 붙잡고 확인할 수 없으니, 오류를 읽을 방법도 설치 전에 준비해야 했습니다.
용어 짧게 정리
- 폴리필: 오래된 브라우저에 없는 API를 JS로 채워 넣는 코드입니다.
- WebView: 앱 안에서 웹 화면을 띄우는 내장 브라우저 영역입니다.
fetch: 브라우저에서 HTTP 요청을 보내는 API입니다.Headers:fetch요청과 응답의 헤더 값을 다루는 객체입니다.window.onerror: 전역 JS 오류를 잡아낼 수 있는 브라우저 이벤트 핸들러입니다.- 반응형 처리: 화면 크기에 맞춰 같은 템플릿을 조정하는 방식입니다. 구형 브라우저에서는 최신 CSS 기능을 그대로 믿기 어렵습니다.