같은 이름, 다른 숫자

광고주가 "클릭 수가 안 맞는다"며 정산을 걸고넘어졌다. 그쪽 대시보드는 1만 클릭, 우리 매체 대시보드는 1만 3천. 돈이 걸린 양면 시장에서 이런 숫자 분쟁은 곧 신뢰 비용이고, 한 번 터지면 광고주도 매체도 데이터 전체를 의심하기 시작한다.

며칠을 파보니 코드에는 버그가 없었다. 양쪽이 같은 "클릭"을 다른 정의로 세고 있었을 뿐이다 — 분모도, 집계 윈도우도, dedup 규칙도 달랐다. 그래서 나는 개별 분쟁을 땜질하는 대신, 우리 플랫폼이 반복해서 밟던 함정 4가지를 정리하고 지표 정의 자체를 양쪽이 합의하는 계약으로 바꿨다.

함정 1 — CTR의 분모

CTR = 클릭 / 노출. 단순해 보이지만 분모(노출)의 정의가 갈린다.

  • 요청(request) 기준: 광고 요청이 온 횟수
  • 응답(response) 기준: 광고를 실제로 내려준 횟수
  • 렌더(rendered) 기준: 클라이언트에서 실제로 그려진 횟수
  • viewable 기준: 화면에 실제로 보인 횟수

같은 캠페인의 CTR이 분모를 무엇으로 잡느냐에 따라 2~3배 차이 난다. 요청 분모 CTR 1%가 viewable 분모로는 3%다.

분모를 명시한다. "CTR"이 아니라 "viewable CTR" / "rendered CTR". 대시보드에 정의를 박는다.

함정 2 — viewability 기준

"광고가 보였다"의 기준 (IAB 표준):

  • 디스플레이: 픽셀의 50%가 1초 이상 노출
  • 비디오: 픽셀의 50%가 2초 이상

그런데 측정 시점이 갈린다.

  • 광고가 viewport에 들어온 순간?
  • IntersectionObserver가 50% threshold를 발화한 순간?
  • 1초 타이머가 완료된 순간?

클라이언트 측정(IntersectionObserver)과 서버 집계 사이에 비동기 지연이 있으면, 사용자가 빠르게 스크롤하면 "들어왔다 나간" 노출이 viewable로 잡힐지 안 잡힐지 경계에서 흔들린다.

→ viewability는 클라이언트에서 1초 타이머 완료 후에만 이벤트 전송. viewport 진입만으로 보내면 과집계.

함정 3 — conversion 윈도우와 늦은 전환

conversion(전환)은 클릭 후 며칠 안의 구매를 센다. 윈도우가 정의를 바꾼다.

  • click-through 7일 / view-through 1일 (흔한 기본)
  • 윈도우가 길수록 conversion ↑ (당연히)

늦은 전환(late conversion) 문제: 클릭은 오늘, 구매는 5일 후. 오늘 집계한 conversion에는 안 잡히고 5일 후 소급 반영된다. 그래서 어제 숫자가 오늘 바뀐다. 실시간 대시보드를 믿은 사람은 "데이터가 변한다"고 항의.

→ conversion 지표에는 집계 기준 시각 + 윈도우를 항상 표기. "5/29 기준, 7일 윈도우"처럼. 소급 반영을 정상 동작으로 설명.

함정 4 — 정산 dedup

정산은 돈이라 가장 엄격하다. 같은 클릭/전환이 두 번 세어지면 과금 분쟁.

중복 발생 원인:

  • 네트워크 재시도 (클라이언트가 같은 이벤트 2번 전송)
  • 사용자 더블 클릭
  • 봇/어뷰징
  • 여러 디바이스의 같은 사용자

→ dedup 키 설계가 핵심. (user_id, ad_id, event_type, time_bucket) 같은 복합 키로 일정 윈도우 내 중복 제거. 멱등성 키 패턴이 그대로 적용된다.

지표 정의를 계약으로

위 4가지 함정은 결국 하나로 모인다. 개별 숫자를 맞추려 들면 끝없는 디버깅이지만, 지표 정의 자체를 명시적 계약으로 올려두면 분쟁의 토대가 사라진다. 그래서 나는 분쟁이 들어올 때마다 답하는 대신, 정의를 한곳에 못 박는 쪽으로 방향을 틀었다.

  • 각 지표의 분모·집계 윈도우·dedup 규칙을 문서화
  • 광고주·매체 양쪽에 공개
  • 측정 도구(MMP) discrepancy 허용 범위를 사전 합의 (보통 ±10%)

정의가 계약이 되자 "숫자 다름"이 더는 항의가 아니라 "정의 차이"로 설명되는 대화가 됐다. 같은 질문에 매번 다시 답하지 않아도 됐다는 뜻이다.

함정

  • 지표 이름만 같으면 같은 값이라 가정: 분모·윈도우가 다르면 다른 값. 이름에 정의를 붙인다.
  • 실시간 대시보드를 정산 기준으로: 늦은 전환으로 숫자가 바뀐다. 정산은 윈도우 마감 후 확정값.
  • 클라이언트 측정을 100% 신뢰: adblock·네트워크 손실·봇으로 클라이언트 이벤트는 항상 누락/과집계. 서버 보정 필요.
  • dedup 윈도우 없음: 무한 dedup은 메모리 폭발. 시간 버킷으로 윈도우 한정.
  • MMP discrepancy를 버그로 취급: 측정 도구가 다르면 ±10%는 정상. 0% 일치를 기대하면 영원히 디버깅.

핵심

양면 시장에서 "숫자가 안 맞는다"는 보통 버그가 아니라 정의 차이다. 코드를 고치는 대신 지표 정의를 양쪽의 계약으로 만들면, 신뢰 비용이 들던 분쟁이 설명 가능한 차이로 바뀐다.

관련

/essays/two-sided-market-decisions — 양면 시장에서 지표가 신뢰 비용인 이유 /essays/data-trust-two-sided — 양쪽이 숫자를 불신하는 구조