App Store Connect의 분석 리포트를 API로 받아 앱별 퍼널을 만드는 스크립트를 몇 달 돌렸습니다. 노출 → 제품 페이지 조회 → 설치 → 체험 → 결제로 이어지는 체인을 뽑아, ASO 작업의 효과를 판정하는 데 썼죠.
매출을 처음 직접 뽑아 대조하다가 숫자가 안 맞았습니다. 판매 리포트의 결제 건수와 분석 리포트의 결제 이벤트가 6배 차이가 났습니다. 판매 리포트는 정확하다고 알려져 있으니 분석 쪽을 팠습니다.
세어 보신 적 있습니까 — 당신의 지표 중 다른 소스와 대조해 본 적 없는 게 몇 개인지. 대조 전에는 발견될 수 없는 종류의 버그가 있습니다. 이게 그거였습니다.
리포트가 직전 3일을 매번 다시 싣는다
ASC의 ONGOING 분석 리포트는 각 processingDate마다 직전 약 3일치를 다시 싣습니다. 정정(restatement)입니다. Date D의 행은 processingDate D+1부터 D+3까지 세 번 등장합니다.
engagement 인스턴스 processingDate 07-11 → Date 07-09, 07-10 포함
engagement 인스턴스 processingDate 07-13 → Date 07-10, 07-11, 07-12 포함
→ Date 07-10 이 두 인스턴스에 모두 등장스크립트는 인스턴스를 전부 합산하고 있었습니다. 그래서 거의 모든 날짜를 23번 계산했습니다. 실측 배율 **2.763.01배.**
진짜 반전은 "비율이 멀쩡했다"는 것
여기까지는 그냥 버그입니다. 진짜 반전은 비율은 전혀 왜곡되지 않았다는 점입니다.
분자와 분모가 같은 배율로 부풀려지니까 PPV(노출→PV)도, 앱 간 상대 순위도, 전환율도 전부 정확했습니다. 몇 주 전 ASO 레버 검증에서 낸 pre/post 배수 판정도 유효했습니다. 망가진 건 절대값과, 절대값으로 만든 결론뿐이었습니다.
그런데 그 하나가 치명적이었습니다. 정확한 소스와 부정확한 소스를 나누는 순간입니다.
- 설치 수는 판매 리포트에서 왔습니다 (정확).
- PV는 분석 리포트에서 왔습니다 (3배).
- PV→설치 = 설치 ÷ PV를 계산하니 **7.1%**가 나왔습니다.
정상 범위가 20% 안팎인 지표에서 7.1%는 명백한 이상입니다. 그래서 "PV→설치가 플릿 최대 누수"라고 진단했습니다. 실제 값은 20.4%, 완전히 정상이었습니다.
한 소스만 3배 틀렸을 때, 그 소스 안에서 계산한 값은 멀쩡하고, 두 소스를 섞은 값만 썩습니다. 그리고 섞은 값이 보통 가장 중요한 지표입니다. 단일 소스 안에서 검산하면 아무 이상도 안 나온다는 게 이 버그가 오래 산 이유입니다.
진짜 병목은 다른 데 있었습니다. 노출 총량이었습니다. 43개 앱을 합쳐 하루 1,735회, 앱당 40회. 전환을 고치는 게 아니라 노출이 없다는 게 문제였는데, 부풀려진 숫자가 그걸 정확히 반대로 보이게 만들었습니다.
부풀림 실측
| 지표 | 부풀린 값 | 실제 |
|---|---|---|
| Quieta 28일 노출 | 2,140 | 734 |
| 플릿 28일 노출 | 144,232 | 48,590 |
| 플릿 28일 PV | 9,218 | 3,190 |
| Biasly 체험 시작 | 5 | 2 |
| 플릿 결제 시작 | 12 | 2 (+체험전환 1, 갱신 1) |
수정 — asc_analytics_engagement.py에 dedupe 추가:
dedupe_restated(instances, date_field, lo, hi)
# processingDate 오름차순 순회
# 키 = (행의 날짜 + 모든 차원 컬럼)
# 마지막 값이 이긴다 (나중 인스턴스가 정정판)
# Counts / Unique Counts 는 키에서 제외
# subs 는 date_field="Event Date"⚠️ days는 인스턴스 수가 아니라 유일 날짜 수로 세야 합니다. 이걸 인스턴스 수로 세면 일평균이 다시 3배 틀립니다.
검증법 한 줄: Quieta 28일 노출이 734로 나오면 dedupe 적용됨. 2,140이면 미적용.
이 버그로 두 달을 판단했다
정직하게 밝힙니다.
- 이 버그로 만든 결론을 실제로 몇 주 들고 다녔습니다. "PV→설치 개선"을 우선순위에 올리고 그 방향으로 작업 계획을 짰습니다. 방향 자체가 틀렸습니다.
- 두 번째 결함이 하나 더 있었습니다. 창 비교 스크립트가 창을 processingDate로 잘랐습니다. restatement 때문에 창이 최대 3일 새어나갑니다. 이제 인스턴스를 넉넉히 받고 행의 Date로 자릅니다. 이전 판정에서 "특정 날짜에 계단 변화"라고 귀속했던 것들은 최대 2일 밀렸을 수 있습니다. 배수 자체는 유효하지만 날짜 귀속은 신뢰도가 떨어집니다.
- 수정이 끝났다고 말할 수 없습니다. 일간 리포트를 만드는 건 다른 머신이고, 이 저장소는 원격이 없는 로컬 전용이라 코드가 자동 전파되지 않습니다. 그쪽 사본을 고치지 않으면 일간 리포트는 계속 3배 값을 냅니다. 패치를 넘겨뒀지만 적용됐는지는 아직 확인 못 했습니다. 이건 코드 문제가 아니라 파이프라인이 두 머신에 쪼개져 있다는 구조 문제입니다.
- 왜 진작 못 봤나. 분석 리포트만 보고 있을 때는 아무 이상이 없었습니다. 다른 소스와 대조하기 전에는 발견될 수 없는 버그였고, 대조를 몇 달 안 했습니다.
같은 날의 반대편 이야기
이 글은 "멀쩡해 보였지만 실제로 3배 틀렸던" 지표를 고친 이야기입니다. 같은 감사에서 정반대 경우도 나왔습니다 — 고장 나 보이는 지표를 추적했더니, 안 고치는 게 정답이었다. 한쪽은 고장처럼 보였지만 의도된 설계라 안 고치는 게 맞았고, 이쪽은 멀쩡해 보였지만 3배 틀려서 반드시 고쳐야 했습니다. 지표를 언제 믿을 것인가라는 한 쌍입니다.
3줄 자가진단
- 소스가 여럿인 파이프라인에서, 두 소스를 나눈 지표를 특히 의심하라. 한 소스만 배율이 틀려도 그 지표만 조용히 썩는다.
- 단일 소스 안의 비율은 배율 오류에 면역이다. 그래서 소스 안에서 검산하면 절대 안 잡힌다. 반드시 다른 소스와 대조하라.
- 시계열 리포트를 합산하기 전에, 같은 날짜가 여러 번 등장하는 정정 구조가 있는지 먼저 확인하라.
솔직한 부분
이 버그의 무서운 점은 결과가 그럴듯했다는 겁니다. 7.1%는 "고칠 수 있는 누수처럼" 보였고, 그래서 저는 실제로 그걸 고치러 갔습니다. 진짜 병목(노출 총량)은 그 착시 뒤에 두 달 숨어 있었습니다.
그 뒤로 매출 숫자는 분석 리포트를 아예 쓰지 않고 정산 리포트만 씁니다 — 그렇게 다시 센 결과가 113일 결제 원장입니다.
지금 당신 대시보드에서 두 시스템의 숫자를 나눠 만든 지표 하나를 떠올려 보세요. 그 분자와 분모, 같은 소스에서 왔습니까?