dist HTML에 굳어버린 404 광고 코드

오늘 본 사고를 적어두려고 합니다.

운영 중인 사이트 101개에서 동시에, AdSense 광고 스크립트의 404가 떴습니다. 정확히는 show_ads_impl.js 같은 종류의 파일이 dist HTML 안에 박혀 있었고, 그 파일이 더 이상 존재하지 않는 경로를 가리키고 있었습니다. 페이지 로딩이 그만큼 늦어지고, 콘솔에는 빨간 줄이 떴습니다.

원인을 찾는 데 한 시간이 안 걸렸습니다.

빌드 과정에 puppeteer로 prerender 단계가 있습니다. SPA 라우팅 사이트들을 정적 HTML로 굳혀서 SEO 점수를 올리기 위한 단계입니다. 이 단계에서 puppeteer가 실제 브라우저를 띄우고 페이지를 한 번 렌더링한 뒤, 그 결과 DOM 을 그대로 HTML 로 저장합니다.

문제는 이 시점에 페이지가 정상 작동을 위해 외부 스크립트를 불러온다는 점입니다. AdSense, Google Analytics, 그 외 광고 네트워크들. 이 스크립트들은 다시 자기들의 하위 스크립트를 부릅니다. show_ads_impl.js 같은 것이 그 중 하나입니다.

puppeteer는 이 모든 요청을 그대로 통과시키고, 페이지 안에 들어온 형태 그대로를 HTML 로 저장합니다. 그러니까 빌드 시점의 show_ads_impl.js URL 이 dist HTML 에 그대로 굳어 들어갑니다.

여기까지는 작동합니다. 그 URL 이 유효한 동안에는.

원인을 찾는 데 한 시간이 걸렸는데, 그 한 시간 동안 101 개 사이트에 들어와서 콘솔 404 를 본 사람이 몇 명인지는 적어두지 않으려고 합니다. 적어두면 또 한참 들여다볼 테니까요. 그러게 진작 분리해뒀어야 했는데, 잘 작동하던 시기가 길었던 만큼 의심할 일도 없었습니다.

문제는 AdSense 측이 그 스크립트 경로를 종종 바꾼다는 점입니다. 빌드 시점에 유효했던 URL 이 며칠 뒤에는 404 가 되어 있을 수 있습니다. 평소에는 사용자 브라우저가 새로 받은 AdSense loader 가 새 URL 로 자동 라우팅해주기 때문에 이런 일이 안 보입니다. 그런데 prerender 로 굳혀버린 HTML 은 그 라우팅을 거치지 않습니다. dist 에 박힌 옛 URL 을 그대로 호출합니다. 그래서 404.

수정은 단순했습니다. puppeteer 설정에서 googlesyndication, googleads, doubleclick, analytics 도메인의 요청을 prerender 단계에서 차단하기로 했습니다. 사용자 브라우저는 이 도메인들을 정상적으로 부르지만, 빌드 시점의 puppeteer 는 부르지 않습니다. 그러면 dist HTML 에는 광고 스크립트가 들어가지 않고, 런타임에 사용자 브라우저가 들어왔을 때 비로소 새 광고 코드를 받아오는 흐름이 됩니다.

101 개 사이트가 동시에 같은 사고를 낸 이유는 단순합니다. 같은 prerender 파이프라인을 공유하기 때문입니다. 한 곳에 들어간 잘못이 101 곳에 그대로 복사됩니다.

지금 와서 생각하면 이 사고는 prerender 와 외부 광고 코드의 관계를 처음부터 분리해두지 않은 것이 원인입니다.

광고 코드를 차단하고 다시 빌드한 뒤 dist 를 재배포했습니다. 101 개 사이트의 콘솔이 다시 깨끗해졌습니다. 사고 자체는 한나절 안에 닫혔지만, "외부 도메인 요청은 prerender 시점에 일단 차단" 이라는 원칙이 머릿속에 한 줄 추가되었습니다. 비싸게 산 한 줄입니다.