크로스 브라우저 테스팅
렌더링 엔진·웹 API·운영체제 차이로 생기는 호환 문제를 분류하고 대상 브라우저와 수동·자동 검증 전략을 세웁니다.
앞 글들에서는 단위 테스트, 통합 테스트, E2E 테스트로 정한 조건에서 기능이 올바르게 동작하는지 확인했습니다. 하지만 웹 애플리케이션은 한 환경에서만 실행되지 않습니다. 사용자는 Chrome, Firefox, Safari, Edge 같은 여러 브라우저를 쓰고, 브라우저마다 렌더링 엔진과 자바스크립트 엔진, 웹 API의 구현이 다를 수 있습니다. 데스크톱의 Chrome과 Edge처럼 같은 엔진을 쓰는 브라우저도 있습니다. 운영체제(Windows, macOS, Linux, Android, iOS)와 기기의 종류(데스크톱, 태블릿, 모바일)에 따라서도 페이지가 보이는 모습과 동작이 달라질 수 있습니다.
이렇게 여러 환경에서 웹 애플리케이션이 올바르고 일관된 경험을 주는지 확인하는 것을 크로스 브라우저 테스팅(cross-browser testing)이라고 합니다. 이 글에서는 호환성 문제가 생기는 원인과 테스트 범위를 정하는 방법, 쓸 수 있는 도구를 정리합니다.
HTML의 레이블과 버튼, CSS 반응형 배치, DOM 이벤트, Promise를 알고 있으면 예제를 따라갈 수 있습니다. 뷰포트(viewport)는 페이지가 배치되는 화면 영역으로, 기기의 전체 해상도와 다를 수 있습니다. 기능 탐지(feature detection)는 브라우저 이름으로 지원 여부를 추측하는 대신, 필요한 API나 CSS 기능이 있는지 직접 검사하는 방식입니다.
호환성 문제가 생기는 원인
웹 표준이 발전하고 브라우저들도 표준을 잘 따르지만, 호환성 문제는 여전히 생깁니다. 먼저 렌더링 엔진이 다릅니다. 데스크톱 Chrome과 Edge는 Blink, Firefox는 Gecko, Safari는 WebKit으로 HTML과 CSS를 해석하고 그리며, 엔진의 작은 차이로 배치나 스타일이 다르게 보일 수 있습니다. 자바스크립트 엔진도 Chrome과 Edge는 V8, Firefox는 SpiderMonkey, Safari는 JavaScriptCore로 서로 다릅니다.
웹 API와 CSS 기능은 브라우저마다 지원 여부와 구현 방식, 버그가 다를 수 있습니다. 일부 CSS 속성은 특정 브라우저에서 -webkit-이나 -moz- 같은 벤더 접두사(vendor prefix)가 필요하기도 한데, 필요한 접두사는 지원할 브라우저와 속성마다 확인해야 하며 모든 CSS에 붙일 필요는 없습니다. 이 밖에도 특정 버전이나 운영체제에서만 생기는 버그, 브라우저마다 다른 보안 정책, 사용자가 바꾼 글꼴 크기와 확대 비율, 설치한 확장 프로그램도 페이지에 영향을 줍니다.
크로스 브라우저 테스팅이 중요한 이유는 지원하기로 한 환경에서 핵심 정보와 작업을 쓸 수 있어야 하기 때문입니다. 모든 픽셀이 똑같을 필요는 없지만, 폼 제출과 결과 확인, 오류 안내 같은 의미와 기능은 유지되어야 합니다(MDN의 크로스 브라우저 테스팅 소개). 특정 브라우저에서 기능이 깨지면 그 사용자들을 잃게 되고 서비스에 대한 신뢰도 떨어집니다. 여러 환경에서 미리 테스트하면 배포 뒤에 문제를 발견하는 일을 줄일 수 있습니다.
지원 범위 정하기
크로스 브라우저 테스팅은 모든 브라우저의 모든 버전을 빠짐없이 확인하는 것이 아니라, 현실적인 범위를 정해 효율적으로 확인하는 것입니다.
FLOWCHART · B99 WEB FUNDAMENTALS
위험 기반 크로스 브라우저 지원 범위
호환성 범위는 브라우저 엔진, API, CSS, 입력 장치와 실제 사용자 비중을 함께 본다. 자동화된 핵심 흐름과 실제 기기 수동 검증을 조합하고 결함은 재현 환경부터 회귀 확인까지 닫는다.
Playwright의 WebKit 빌드는 설치된 Safari나 실제 iOS 기기를 그대로 대신하지 않는다. 기기·OS 의존성이 큰 흐름은 실제 대상에서 별도 확인한다.
위 그림은 지원 범위를 정하고 검증하는 흐름입니다. 먼저 실제 사용자가 어떤 브라우저를 얼마나 쓰는지와 핵심 사용자 흐름이라는 사용자 데이터에서 시작해, 지원할 엔진과 버전, 기능을 담은 지원 기준표를 만듭니다. 그다음 각 흐름의 실패 위험을 실패했을 때의 영향과 재현하기 어려운 정도로 평가합니다.
위험이 낮거나 이미 알려진 차이라면 Chromium, Firefox, WebKit 엔진에서 자동화 테스트로 지원 범위의 기능을 확인합니다. 위험이 높거나 기기에 따라 달라지는 흐름이라면 자동화와 함께 실제 기기에서 입력 방식, GPU, 운영체제의 조합을 확인합니다. 두 경로는 같은 조건으로 다시 검증하는 단계에서 만나고, 고친 전후의 결과로 기준표를 갱신합니다. 그림 아래의 설명처럼 Playwright의 WebKit 빌드는 설치된 Safari나 실제 iOS 기기를 그대로 대신하지 않으므로, 기기와 운영체제에 크게 의존하는 흐름은 실제 대상에서 따로 확인해야 합니다.
지원 기준표 만들기
가장 먼저 어떤 브라우저와 버전을 지원할지 분명히 정합니다. 웹 분석 도구로 실제 사용자가 쓰는 브라우저와 기기를 보고, 회사에서 관리하는 브라우저나 특정 앱의 WebView처럼 따로 지원해야 하는 환경이 있는지 확인합니다. 쓰고 있는 라이브러리가 지원하는 브라우저 범위도 고려합니다.
최소 지원 버전은 사용자 비중과 핵심 기능, 지원 비용을 보고 정합니다. 최신 버전과 그 이전 한두 버전을 지원하는 것은 가능한 정책의 한 예일 뿐 모든 프로젝트의 기준은 아닙니다. 오래된 환경에는 기본 기능을 먼저 주고 지원하는 환경에서 기능을 더하는 점진적 향상(progressive enhancement)이나, 브라우저에 없는 기능을 코드로 채워 주는 폴리필(polyfill)을 필요한 범위만큼 검토합니다. 지원 기준표는 한 번 정하고 끝나는 표가 아니라, 사용자 데이터와 장애 기록을 보며 계속 고쳐 나가는 기준입니다.
예를 들어 가입 폼 제출의 테스트 계획은 다음처럼 쓸 수 있습니다. 이미 통과한 기록이 아니라 계획의 예이며, 실제 기록에는 “최신” 대신 실제 브라우저와 운영체제의 버전, 빌드, CSS 픽셀 단위의 뷰포트, 확대 비율, 입력 방식을 적습니다.
| 대상과 환경 | 입력 조건 | 확인할 것 | 남는 확인 |
|---|---|---|---|
| Windows의 지원 Chrome과 Firefox | 데스크톱 너비, 키보드만 사용 | 레이블, 포커스, 오류 메시지, 제출 결과 | 터치와 모바일 키보드 |
| Playwright WebKit, 너비 390 CSS px | 자동화한 입력과 제출 | 좁은 화면에서 내용, 버튼, 결과가 유지됨 | 실제 Safari와 iOS의 동작 |
| 지원하는 iPhone의 실제 Safari | 터치와 화면 키보드 | 키보드가 올라와도 입력, 제출, 오류 확인 가능 | 다른 기기와 운영체제 버전 |
모든 환경에서 같은 빌드와 같은 테스트 데이터를 씁니다. 실패를 재현하려면 URL, 동작 순서, 기대한 결과와 실제 결과도 함께 기록해야 합니다. 한 환경에서 통과했다고 그 결과를 표의 다른 환경에 그대로 옮겨 적지 않습니다.
더 깊이: 기능 단위로 지원 범위를 말하는 Baseline
지원 기준표가 브라우저와 버전을 기준으로 한다면, 반대로 기능 하나를 기준으로 어디까지 쓸 수 있는지 말하는 방법도 있습니다. 그중 하나가 Baseline으로, Chrome 팀이 시작했고 지금은 W3C의 WebDX 커뮤니티 그룹이 정의합니다. 기준이 되는 브라우저는 Chrome, Edge, Firefox, Safari이며, Android의 Chrome과 Firefox, iOS의 Safari처럼 모바일 버전도 포함합니다.
Baseline은 기능을 세 단계로 나눕니다. 핵심 브라우저 가운데 하나라도 지원하지 않으면 제한적 지원(limited availability), 모든 핵심 브라우저의 최신 버전이 지원하기 시작하면 새로 지원(newly available), 그 시점에서 30개월이 지나면 대부분의 사이트가 지원 걱정 없이 쓸 수 있다는 뜻의 널리 지원(widely available)입니다. MDN의 각 기능 문서 맨 위에 보이는 Baseline 표시가 이 기준을 따릅니다.
그래서 팀의 지원 기준을 정할 때 널리 지원 단계의 기능은 추가 확인 없이 쓰고, 새로 지원 단계의 기능은 대체 경로를 함께 준비하는 식으로 규칙을 세울 수 있습니다. 다만 Baseline은 핵심 브라우저의 지원 여부만 보므로, 다른 브라우저나 오래된 기기가 많은 서비스라면 실제 사용자 데이터로 정한 지원 기준표를 함께 봐야 합니다. 정의는 web.dev의 Baseline 설명에 있습니다.
테스트 방식 조합하기
수동 테스트는 실제 사용자의 경험에 가장 가깝고, 디자인과 배치의 미묘한 차이를 잘 찾아냅니다. 하지만 시간이 많이 들고 반복하기 지루해 실수가 생기기 쉬우므로, 복잡한 화면 상호작용이나 중요한 사용자 흐름, 시각적인 완성도가 중요한 화면을 주요 브라우저에서 확인하는 데 씁니다.
자동화 테스트는 빠르고 반복할 수 있으며 결과가 일관됩니다. 변경을 통합할 때마다 빌드와 테스트를 자동으로 돌리고 릴리스 준비나 배포까지 이어 주는 CI/CD 파이프라인에 넣으면 코드가 바뀔 때마다 실행할 수 있지만, 시각적인 미묘한 차이까지 잡아내기는 어렵습니다. 주로 기능이 올바른지, API 호출과 데이터 처리가 맞는지를 Cypress, Playwright, Selenium 같은 E2E 도구로 여러 브라우저에서 확인합니다.
정적 분석과 빌드 도구도 함께 씁니다. ESLint와 Stylelint는 코드 규칙을 지키게 하고, eslint-plugin-compat 같은 호환성 검사 플러그인을 더하면 지원 대상 브라우저에 없는 기능도 미리 찾아 줍니다. Can I Use 사이트에서는 CSS 속성이나 자바스크립트 API를 어느 브라우저가 지원하는지 확인할 수 있습니다. Babel은 지원할 브라우저에 맞춰 자바스크립트 문법을 바꿔 주지만, 문법을 바꾼다고 Promise 같은 실행 기능이나 웹 API가 생기지는 않으므로 필요한 폴리필은 따로 확인합니다(Babel preset-env 문서). PostCSS 플러그인인 Autoprefixer는 Browserslist에 적은 지원 대상에 필요한 CSS 접두사를 자동으로 붙여 주지만, 지원하지 않는 배치 기능 자체를 만들어 주지는 않습니다(Autoprefixer 공식 저장소).
테스트 환경
가장 정확한 결과는 실제 기기와 브라우저에서 얻을 수 있지만, 모든 기기를 갖추기는 어렵습니다. VirtualBox 같은 가상 머신이나 Android Studio, Xcode의 에뮬레이터와 시뮬레이터로 여러 운영체제 환경을 만들 수 있습니다. BrowserStack이나 Sauce Labs 같은 클라우드 테스트 서비스를 쓰면 많은 브라우저, 운영체제, 기기 조합에서 테스트를 실행하고 스크린샷이나 동영상을 받아 볼 수 있습니다.
결함을 재현하고 닫기
PROCESS
호환성 결함 재현·수정·회귀 루프
브라우저 버전·OS·기기를 고정해 재현하고 실패 경계를 좁힌다. 최소 수정이나 대체 기능을 적용한 뒤 지원 범위에서 다시 검증한다.
지원 범위의 기능 검사가 모두 통과하면 재현 조건·수정·검증 근거를 기록해 결함을 닫는다. 실패하면 새 증거로 실패 경계 분리 단계에 돌아간다.
- 재현·분리
- 정확한 환경·실패 layer
- 회귀 실패 feedback
위 그림은 호환성 결함을 찾아 고치고 다시 확인하는 순환입니다. 먼저 브라우저와 버전, 운영체제, 기기를 정확히 고정하고, 같은 빌드와 URL, 데이터, 동작 순서로 같은 증상을 재현합니다. 그다음 문제가 배치, 이벤트, 웹 API, 정책 가운데 어느 층에서 생기는지 좁혀 나갑니다.
원인을 찾으면 기능을 그대로 지킬 수 있는지 묻습니다. 지킬 수 있다면 CSS나 이벤트 처리, API 사용 전 검사 같은 표준적인 방법으로 가장 작게 고칩니다. 지킬 수 없다면 기능 탐지로 지원 여부를 확인해 대체 화면이나 다른 사용 방법을 줍니다. 두 경로는 지원 범위 전체에서 엔진 자동화와 실제 기기로 다시 검증하는 단계에서 만납니다. 모두 통과하면 재현 조건, 수정 내용, 검증 결과를 기록해 결함을 닫고, 실패하면 새로 얻은 정보를 들고 원인을 좁히는 단계로 돌아갑니다.
개발자 도구 활용
거의 모든 브라우저에는 쓸모가 많은 개발자 도구가 들어 있습니다. 반응형 디자인 모드로 화면 크기를 바꿔 좁은 배치를 확인할 수 있고, 콘솔과 네트워크, 성능 탭에서 브라우저별 자바스크립트 오류와 네트워크 요청, 렌더링 성능을 직접 볼 수 있습니다. Android나 iOS 기기를 USB로 연결하면 데스크톱 브라우저의 개발자 도구로 모바일 페이지를 원격 디버깅할 수도 있습니다.
다만 반응형 디자인 모드나 Playwright의 기기 설정은 뷰포트, 사용자 에이전트, 터치 지원 같은 값을 흉내 낼 뿐, 실제 기기의 운영체제나 화면 키보드, GPU를 그대로 실행하지는 않습니다(Playwright의 에뮬레이션 문서).
자동화 도구
E2E 테스트 도구는 크로스 브라우저 테스트에도 씁니다. Cypress는 기본으로 들어 있는 Electron 외에 Chrome 계열 브라우저와 Firefox에서 테스트를 실행할 수 있습니다. Playwright는 Chromium, Firefox, WebKit과 설치된 Chrome, Edge를 하나의 API로 테스트하지만, 패치된 WebKit 빌드를 쓰므로 설치된 Safari나 실제 iOS 기기에서 검증한 것과 같지는 않고, 운영체제에 따라 미디어 코덱 같은 기능도 다를 수 있습니다(Playwright의 브라우저 문서). Selenium WebDriver는 브라우저마다 제공되는 WebDriver로 사용자 동작을 자동화하며, 실제로 지원되는 조합은 드라이버와 브라우저, 운영체제의 조건을 확인해야 합니다(Selenium 공식 문서).
폴리필과 Autoprefixer
앞에서 말한 폴리필은 새 자바스크립트 기능이나 웹 API를 지원하지 않는 브라우저에서 같은 기능을 쓸 수 있게 채워 주는 코드입니다. core-js 같은 관리되는 패키지로 지원 대상에 필요한 ECMAScript 폴리필을 빌드에 넣을 수 있지만, 패키지가 제공하지 않는 웹 API는 따로 구현하거나 지원 여부를 확인해야 합니다. CSS 쪽에서는 앞에서 본 Autoprefixer가 필요한 벤더 접두사를 자동으로 붙여 줘 접두사 문제를 크게 줄여 줍니다.
사례: 복사 기능과 대체 경로
공유할 문장을 복사하는 버튼이 있다고 가정합니다. 클립보드 API가 있더라도 권한이나 보안 조건 때문에 호출이 실패할 수 있으므로, API가 있는지 확인하는 것과 호출이 성공했는지 확인하는 것을 나눠야 합니다. navigator.clipboard.writeText()는 HTTPS 같은 안전한 컨텍스트에서만 쓸 수 있고, 쓰기가 허용되지 않으면 거부될 수 있습니다(MDN의 Clipboard.writeText()).
다음은 별도의 연습 페이지용 코드입니다. HTML 다음에 스크립트를 실행하며, 자동 복사에 실패하면 같은 내용을 직접 선택해 복사할 수 있게 합니다.
<label for="share-text">공유할 문장</label>
<textarea id="share-text" readonly>테스트할 문장</textarea>
<button id="copy-button" type="button">문장 복사</button>
<p id="copy-status" role="status"></p>async function copyText(text, clipboard) {
if (!clipboard || typeof clipboard.writeText !== 'function') return false;
try {
await clipboard.writeText(text);
return true;
} catch {
return false;
}
}
const field = document.querySelector('#share-text');
const status = document.querySelector('#copy-status');
document.querySelector('#copy-button').addEventListener('click', async () => {
const copied = await copyText(field.value, navigator.clipboard);
status.textContent = copied
? '복사했습니다.'
: '자동 복사를 사용할 수 없습니다. 문장을 선택해 직접 복사하세요.';
if (!copied) {
field.focus();
field.select();
}
});결과는 세 갈래입니다. API가 없거나 호출이 거부되면 성공 문구가 나오지 않고, 문장이 선택되어 직접 복사할 수 있어야 합니다. 호출이 성공하면 쓰기가 끝난 뒤에 성공 안내가 나와야 합니다. copyText()에 성공하거나 거부하는 대역을 넘기면 이 분기는 단위 테스트로 확인할 수 있습니다. 하지만 실제 클립보드에 붙여 넣었을 때 같은 문장이 나오는지, 터치로 직접 선택할 수 있는지는 지원하는 브라우저와 기기에서 따로 확인해야 합니다. 브라우저 이름만 보고 성공 여부를 예측하지 않는 것이 기능 탐지의 핵심입니다.
정리
- 브라우저마다 렌더링 엔진, 자바스크립트 엔진, 웹 API의 구현이 다르고, 운영체제와 기기, 사용자 설정도 페이지에 영향을 줍니다.
- 크로스 브라우저 테스팅은 모든 조합이 아니라 사용자 데이터로 정한 지원 기준표의 범위에서, 핵심 의미와 기능이 유지되는지 확인합니다.
- 위험이 낮은 흐름은 엔진 자동화로, 기기에 따라 달라지는 흐름은 자동화와 실제 기기로 함께 확인합니다.
- 결함은 정확한 환경과 같은 증상을 고정해 재현하고, 실패한 층을 좁혀 가장 작게 고치거나 대체 경로를 준 뒤 지원 범위 전체에서 다시 검증합니다.
- Babel은 문법을, Autoprefixer는 CSS 접두사를 다룰 뿐이며, 없는 웹 API는 폴리필이나 기능 탐지와 대체 경로로 따로 다뤄야 합니다.
- 기기 에뮬레이션과 Playwright의 WebKit은 실제 기기와 Safari를 그대로 대신하지 않습니다.
연습 문제
1. 기기 모드의 통과 기록
Chrome 개발자 도구의 기기 모드에서 너비 390px로 가입 폼을 확인했고 문제가 없었습니다. 이 결과를 “iPhone Safari 검증 완료”로 기록할 수 있을까요? 아직 남은 조건 두 가지를 적어 보세요.
해설 보기
기록할 수 없습니다. 이 결과는 그 Chrome 버전과 뷰포트에서만 통과한 기록입니다. 실제 Safari와 iOS 버전에서의 동작, 그리고 터치 입력과 화면 키보드가 올라온 상태는 아직 확인하지 않았습니다.
“빌드 X, Windows Chrome 버전 Y, 뷰포트 390×844 CSS px, 마우스 입력으로 폼 제출 확인, 실제 iPhone Safari는 확인 전”처럼 실제로 관찰한 범위만 적습니다.
2. API는 있지만 거부될 때
복사 예제에서 navigator.clipboard.writeText는 있지만 호출한 Promise가 거부되었습니다. 화면에는 무엇이 보여야 하고, 어떤 확인이 남을까요?
해설 보기
“자동 복사를 사용할 수 없습니다. 문장을 선택해 직접 복사하세요.”라는 안내가 보이고, 입력란에 포커스가 가며 문장이 선택되어야 합니다. copyText()가 catch에서 false를 돌려주므로 성공 안내는 나오지 않습니다.
거부하는 대역으로 이 분기를 테스트해 통과했더라도, 실제 대상 브라우저에서 권한과 HTTPS 조건, 그리고 선택한 문장을 직접 복사해 붙여 넣을 수 있는지는 다시 확인해야 합니다.
3. 빌드 도구로 해결되는 것
Babel과 Autoprefixer를 프로젝트에 적용했습니다. 아직 지원되지 않는 웹 API 문제와, 화면 키보드에 가려 제출 버튼을 누를 수 없는 문제도 해결되었다고 말할 수 있을까요?
해설 보기
말할 수 없습니다. 문법 변환과 CSS 접두사 생성, 웹 API의 제공, 실제 기기에서의 배치는 서로 다른 문제입니다.
지원되지 않는 API에는 기능 탐지와 필요한 대체 경로를 더하고, 가려진 제출 버튼은 화면 키보드가 올라온 상태에서 실제 기기로 배치와 입력을 확인하며 고쳐야 합니다.
다음 글 고급 디버깅 기법에서는 브라우저 개발자 도구의 중단점과 콜 스택, 네트워크 탭을 활용해 버그의 원인을 체계적으로 찾는 방법을 살펴봅니다.