검색 트래픽의 대부분은 사람이 아니었다
글: Oleg Sidorkin, Cinevva CTO
우리의 자연 검색 트래픽은 성장하고 있으며, 올해 내내 그래 왔다. 그 부분은 사실이다. 하지만 7월 초, 우리가 출시한 어떤 것과도 맞아떨어지지 않는 방식으로 그래프가 급등했다. 원인 없는 급등이라면 오후 한나절을 들여 살펴볼 가치가 있다.
조사 결과 Google 리퍼러를 달고 들어오는 헤드리스 Chrome 봇 군단을 발견했다.
정점보다 중요한 것은 그래프의 형태다
봄 동안 가짜 트래픽의 비율은 전체 세션의 3분의 1에서 3분의 2 사이를 오갔다. 공개 웹사이트라면 어디든 겪는 배경 잡음과 대략 비슷한 수준이다.
6월 말부터는 비율이 오르기 시작해 계속 상승했다. 4월 첫째 주를 기준으로 보면 7월 중순까지 실제 세션은 약 5.5배 증가했다. 자동화된 세션은 약 18.6배 증가했다.
발견하기 어려웠던 이유
우리는 이 수치를 면밀히 살펴보며, 설명 없이 움직이는 지표가 있으면 곧바로 쿼리를 작성해 확인한다. 이번 문제를 제대로 파헤치는 데 시간이 걸린 이유는 세션 행만으로는 두 집단을 실제로 구분할 수 없었기 때문이다.
사용자 참여도는 각 세션의 last_activity_at 타임스탬프로 추론하고 있었다. 한 페이지만 방문한 경우 이 필드는 처음 기록된 뒤 갱신되지 않는다.
따라서 한 페이지를 불러오고 떠나는 봇과 가이드에 들어와 5분 동안 읽는 사람이 똑같은 행을 기록한다. 이탈률, 사이트 체류 시간, 참여도 등 모든 지표가 단일 페이지 세션에 관해서는 아무 정보도 담고 있지 않은 필드에서 계산됐다. 드문 일도 아니다. 자체 구축한 분석 시스템 대부분이 기본적으로 이런 구조이며, 여러분의 시스템에서도 내가 가장 먼저 확인할 부분이다.
해결책은 세션 행에서 추론하는 일을 멈추고, 클라이언트에서 실제 페이지 체류 시간과 스크롤 깊이를 보고하는 종료 비컨을 대신 읽는 것이었다. 추론 대신 증거를 사용하자 두 집단은 즉시 분리됐다.
| 분류된 세션 | 그 외 모든 세션 | |
|---|---|---|
| 한 번이라도 스크롤함 | 1.8% | 56.5% |
| 로그인한 계정에 속함 | 0.05% | 29% |
이 격차는 조용히 방문한 사용자를 로봇으로 잘못 분류한 것이 아니라, 분류기가 실제 현상을 포착하고 있다는 증거다.
봇 군단의 정체를 드러내는 단서
사용자 에이전트는 가장 쉽게 바꿀 수 있기 때문에 최악의 확인 지점이다. 가장 좋은 신호는 그래픽 카드로 밝혀졌다.
데이터 센터의 헤드리스 브라우저에는 GPU가 없으므로 소프트웨어 렌더링으로 대체되며, WebGL은 두 번 물을 필요도 없이 렌더러 문자열을 보고한다. SwiftShader, llvmpipe, Mesa OffScreen 같은 것들이다. 실제 방문자에게는 실제 GPU가 있다.
| 신호 | 포착하는 대상 | 전체 증거에서의 비율 |
|---|---|---|
| 소프트웨어 GPU 렌더러 | 물리적 그래픽 카드가 없음 | 90.5% |
| 데이터 센터 또는 프록시 ASN | 임대한 네트워크 출처 | 58% |
navigator.webdriver | 활성화된 채로 남은 자동화 플래그 | 10.4% |
네트워크 출처는 예상보다 도움이 덜 됐다. 이 세션 중 약 40%는 실제 소비자용 ISP를 통해 나갔다. 이제 주거용 프록시는 저렴하며, 봇이 AWS에서 온다고 가정하는 규칙은 거의 절반을 놓치게 된다.
나머지 단서는 클라이언트가 자신에 관해 보고한 정보 속 물리적으로 불가능한 조합에서 나왔다. Chromium 사용자 에이전트는 항상 AppleWebKit 토큰과 일치하는 Safari 토큰으로 끝나며, 그 값은 2013년부터 537.36으로 고정돼 있다. 하지만 이 봇 군단은 Safari/537.35와 .38 같은 오타를 내보냈다. 휴대폰 사용자 에이전트가 1600x1200 화면을 주장했고, 또 다른 변형은 완벽한 정사각형인 1600x1600을 주장했다. 1200x3000이라고 주장하는 경우도 있었는데, 누구도 생산하지 않는 2.5:1 비율의 세로형 데스크톱 모니터인 셈이다.
이 모든 것은 누군가의 에뮬레이터 설정이며, 직접 찾아보지 않으면 어느 것도 눈에 띄지 않는다.
분류할 때마다 봇 군단은 적응했다
첫 번째 프로필은 Linux Chrome, Google 리퍼러, America/New_York, 1920x1080이었다. 우리는 규칙을 작성했다.
그러자 UTC 시간대에서 800x600을 사용하는 변형이 돌아왔다. 규칙의 범위를 넓혔다.
그다음에는 China Mobile과 Unicom의 주거용 주소를 이용하며 특정 브라우저 빌드에 고정된 중국 집단이 나타났다. 여기에는 2022년 3월의 Chrome 99와 Chrome DevTools 기기 에뮬레이션 기본값인 2018년 WeChat을 실행하는 2015년형 Nexus 5도 포함됐다. 이들은 소비자용 ISP를 통해 나갔기 때문에 우리가 가진 모든 네트워크 규칙이 무용지물이었다.
그 뒤에는 Cox와 Comcast에서 최신 버전이며 전적으로 그럴듯한 Chrome 146 사용자 에이전트와 1920x1080 해상도를 사용하는 미국형 변종이 나타났다. 이 핑거프린트에는 잘못된 점이 하나도 없으며, 세션을 하나씩 보면 실제 방문자와 구분할 수 없다.
그래서 마지막으로 만든 탐지기는 개별 세션을 보지 않는다. 이전 48시간의 세션을 국가, 사용자 에이전트, 화면 크기, 트래픽 출처별로 그룹화한 다음, 익명 세션이 20개 이상이면서 그중 95% 이상이 3초도 읽지 않고 단 한 픽셀도 스크롤하지 않은 클러스터를 찾는다.
실제 방문자는 이렇게 촘촘하게 군집하지 않는다. 완전히 같은 브라우저와 해상도를 사용하는 낯선 사람 20명이 모두 아무것도 읽지 않고 떠났다면, 그것은 주소 20개를 사용하는 하나의 머신이다. 클러스터 안에서도 실제로 읽거나 스크롤한 사용자는 사람으로 유지되며, 실제 계정에 연결된 세션은 아예 분류하지 않는다.
감사 과정에서 정반대의 오류도 발견했다
수치에서 가짜 방문자를 걷어내는 과정에서, 그동안 버리고 있던 실제 방문자도 발견했다.
실제 읽기 시간을 확보하고 보니 사람의 이탈로 집계했던 세션 중 34.2%는 30초 이상 읽었거나 페이지의 절반 넘게 스크롤한 사용자였다. ‘실패’라고 여겼던 세션의 3분의 1은 사실 콘텐츠가 제 역할을 한 결과였다. Bing에서 들어온 중국 독자들은 하나의 가이드를 5분 동안 읽고도 이탈로 기록되고 있었다.
수치는 동시에 양쪽 방향으로 잘못돼 있었고, 두 오류 모두 같은 잘못된 결론을 가리켰다. 실제 트래픽은 더 적지만 참여도는 더 깊은데도, 두 오류가 합쳐져 트래픽 규모는 크고 깊이는 얕다고 말해주고 있었다.
비용을 지불한 채널이 오히려 정직했다
지난 30일 기준. 해당 기간 전체 세션 중 Google 자연 검색이 63.7%를 차지했다.
성장 업무에서 통용되는 모든 직감은 정반대를 가리킨다. 사기가 일어나는 곳은 유료 채널이고, 자연 검색은 직접 얻어낸 성과라고 생각하기 마련이다. 우리에게는 그 반대였다.
7월 수치를 바탕으로 예산을 재배분했다면, 실제 사람을 데려오던 채널에서 돈을 빼서 스크레이퍼를 위한 콘텐츠를 더 만드는 데 투입했을 것이다.
오후 한나절을 들일 가치가 있는 이유
숫자 자체는 그저 숫자일 뿐이다. 하지만 그 하류에는 다음에 작성할 페이지, 투자할 언어권, 캠페인의 심사 통과 여부, 그리고 같은 일을 계속해도 될 만큼 분기 실적이 좋아 보이는지가 달려 있다. 값싼 프록시 풀을 가진 낯선 사람 하나가 이 모든 것을 움직일 수 있다.
작은 사이트를 운영한다면 자신의 성장 곡선을 믿기 전에 GPU 렌더러 문자열과 종료 비컨부터 살펴보길 바란다.
그리고 우리의 수치는 결론이 아니라 하한선으로 봐야 한다. 여기서 ‘사람’은 단지 ‘아직 적발되지 않음’을 뜻하고, 규칙은 우연히 관찰한 핑거프린트를 바탕으로 작성됐으며, 우리는 여전히 누가 이 봇 군단을 운영하는지, 게임 제작 사이트에서 무엇을 원하는지 모른다.
관련 글:
- 고정비의 함정 — 배후에 충격을 흡수할 장치가 전혀 없었던 또 하나의 숫자
- 자체 WebGPU 엔진을 만든 이유 — 의존하는 계층을 직접 소유해야 한다는 같은 논지
- 가이드 — 이 감사에서 측정하려 했던 콘텐츠