오디오 테스트는 화면 말고 AnalyserNode 로 — 백그라운드 탭에서 17개가 전부 「무음」 으로 나온 이유
그림은 멈춰도 소리는 난다. 피크 값을 재면 클리핑까지 잡힌다
오디오 코드는 화면이 아니라 AnalyserNode 로 검사한다. 백그라운드 탭에서는 그림이 멈춰도 소리는 난다. 그리고 피크 값을 재면 「소리가 나나」 뿐 아니라 「깨지나(클리핑)」 까지 잡힌다. 이 글의 스니펫 열 줄이면 오디오 데모를 자동으로 검사할 수 있다.
17개가 전부 무음이었다
학습용 앱에 실행 상자 17개를 만들고, 전부 소리를 내는지 브라우저 자동화로 확인하려 했다. 처음엔 화면의 오실로스코프 캔버스를 읽어서 「파형이 그려졌나」 로 판단했다. 결과는 17개 전부 무음. 그런데 손으로 누르면 다 소리가 났다.
자동화가 띄운 탭은 document.visibilityState = "hidden", 즉 백그라운드 탭이었다. 거기서는 이런 일이 생긴다.
| 백그라운드 탭에서 | |
|---|---|
requestAnimationFrame |
멈춤 → 캔버스 오실로스코프가 안 그려짐 |
Turbo.visit |
화면 교체가 rAF 에 걸려 있어 turbo:load 가 안 온다 |
Tone.getDraw().schedule |
rAF 기반이라 실행 안 됨 |
setInterval |
1초 단위로 늦춰짐 |
AudioContext |
계속 돈다. 소리는 난다 |
그림을 그리는 쪽만 멈추고, 소리를 만드는 쪽은 멀쩡했다. 화면을 보고 판단하면 틀릴 수밖에 없다.
검사 코드
페이지가 만드는 analyser 를 가로채서 잡아 두고, 시간 영역 데이터의 절댓값 최댓값을 본다.
const orig = BaseAudioContext.prototype.createAnalyser
BaseAudioContext.prototype.createAnalyser = function () {
const a = orig.call(this)
window.__lastAnalyser = a
return a
}
const peak = () => {
const buf = new Float32Array(__lastAnalyser.fftSize)
__lastAnalyser.getFloatTimeDomainData(buf)
return buf.reduce((m, v) => Math.max(m, Math.abs(v)), 0)
}
// 0 이면 무음, 1 을 넘으면 클리핑
페이지에 analyser 가 없으면 검사용으로 하나 만들어 출력 앞에 끼우면 된다. 페이지 스크립트보다 먼저 이 코드를 주입해야 가로챌 수 있다.
재 보니 나온 것
로컬 Chrome, 백그라운드 탭(2026-09-19). 캔버스 기준으로는 17개 전부 무음(오판)이었다. AnalyserNode 기준으로는 코드에 적은 볼륨이 그대로 나왔다. gain 0.2 → peak 0.2, 0.3 → 0.3, 0.15 → 0.15.
덤으로 클리핑이 3건 나왔다. 귀로 들을 땐 몰랐던 것들이다.
직접 짠 16스텝 드럼 머신: 1.17. 킥 0.8 + 스네어 0.4 + 햇 0.2 가 같은 박에 겹쳤다
Tone.js Synth 기본 볼륨(0dB): 0.99. 아슬아슬하다
Tone.js 베이스 + 킥: 1.23
전부 볼륨을 내려서 고쳤다. 디지털 오디오에서 ±1 을 넘는 값은 잘려 나가고, 그게 「찌직」 하는 깨짐이다. 소리 여러 개가 같은 순간에 겹치는 드럼은 특히 잘 넘는다.
다른 방법과 비교하면
| 방법 | 백그라운드 탭 | 클리핑 | 메모 |
|---|---|---|---|
| 사람이 듣기 | – | 놓치기 쉽다 | 0.99 와 1.17 은 귀로 구분이 어렵다 |
| 캔버스 파형 읽기 | 오판 | 못 잡는다 | rAF 가 멈춘다 |
| AnalyserNode 피크 | 된다 | 잡는다 | 페이지에 analyser 가 있거나 끼워야 한다 |
「소리 기능 E2E 테스트」 를 처음 짤 때 스크린샷 비교부터 떠올리기 쉬운데, 오디오는 오디오 쪽에서 재는 게 맞다.
아직 확인 못 한 것
AudioContext 는 스크립트로 버튼을 눌러 만들었는데도 running 이었다. 그 전에 실제 클릭이 한 번 있었기 때문으로 보인다(sticky activation, 한 번 사용자 조작이 있으면 그 뒤로 남는 허가). 실제 클릭 없이도 running 이 되는지, Playwright 헤드리스에서 같은 검사가 되는지, OfflineAudioContext 로 렌더해서 CI 에 넣을 수 있는지는 아직이다.
오디오가 들어간 웹 기능을 자동 테스트로 묶고 싶다면 negabaro 에 상담해도 된다. 우리도 이 검사로 실제 버그를 세 개 잡았다.
핵심 포인트
백그라운드 탭에서는 rAF 가 멈춰 캔버스 파형으로 판단하면 무음으로 오판한다
createAnalyser 를 가로채 getFloatTimeDomainData 의 절댓값 최댓값을 본다
피크 0 은 무음, 1 초과는 클리핑 — 드럼처럼 겹치는 소리에서 잘 나온다