마케팅 데이터 분석

GA4·MMP·주문 매출이 다를 때 먼저 맞춰야 할 지표 정의

구매 로그 5행이 실제 주문 3건, 구매자 2명이 되는 합성 예제로 마케팅 지표의 집계 단위·시간대·중복·환불을 확인합니다. GA4와 BigQuery, MMP 데이터를 비교하기 전에 정할 기준과 Python 재현 코드를 담았습니다.

구매 로그는 5줄인데 실제 주문은 3건, 구매자는 2명입니다. 매출도 로그를 그대로 더하면 289,900원이고, 주문과 환불을 확인하면 160,000원입니다. 숫자만 나란히 놓으면 수집 오류처럼 보이지만, 무엇을 세었는지부터 확인해야 합니다.

아래 숫자는 집계 차이를 설명하기 위해 만든 합성 데이터입니다. 실제 고객이나 캠페인의 성과를 사용하지 않았습니다. Python 예제와 검증 코드를 함께 공개해 같은 결과를 직접 확인할 수 있게 했습니다.

표는 자체 수집 로그를 단순화한 형식입니다. GA4나 MMP에서 제공하는 데이터의 스키마와 자동 중복 제거 동작은 해당 수집 경로에서 별도로 확인해야 합니다.

구매 이벤트 수와 주문 수는 왜 다른가

구매 이벤트는 결제와 관련해 기록한 행동입니다. 주문은 거래이고, 구매자는 그 거래를 한 사람입니다. 한 사람이 두 번 주문할 수도 있고, 한 주문의 구매 이벤트가 두 번 전송될 수도 있습니다. 세 지표를 모두 ‘구매 수’로 부르면 리포트를 비교할 때부터 오해가 생깁니다.

단일 상점에서 다음과 같은 구매 로그가 들어왔다고 가정합니다. 금액은 모두 원화이고, 표의 시각은 한국 시간입니다. 이 예제에서는 상품 금액만 사용하며 세금·배송비는 별도 항목으로 계산하지 않습니다.

이벤트주문구매자결제 시각금액확인할 내용
e1o101A9월 2일 00:05100,000원정상 주문
e2o101A9월 2일 00:05100,000원같은 주문의 중복 기록
e3o102B9월 2일 12:0050,000원9월 4일에 20,000원 부분 환불
e4o103A9월 2일 13:0030,000원A의 두 번째 주문
e5o104C9월 2일 14:009,900원테스트 결제
e6o105D9월 3일 00:3080,000원분석 날짜 밖의 주문

9월 2일에 해당하는 로그는 e1부터 e5까지 5행입니다. 주문 번호로 중복을 제거하면 4건이 남고, 테스트 결제를 제외하면 3건입니다. 구매자는 A와 B 두 명입니다. 이벤트가 5개라는 사실에서 주문이 5건이라는 결론을 바로 낼 수는 없습니다.

같은 데이터에서 서로 다른 단위를 셉니다9월 2일 구매 로그5행주문 번호 중복 제거4건테스트 주문 제외3건구매자 A가 2건, B가 1건 주문했으므로 구매자는 2명입니다.합성 데이터 · 단일 상점 · 한국 시간 · KRW
주문 수와 구매자 수의 차이에는 정상적인 재구매도 포함됩니다.

실제 데이터에서는 주문 번호가 어느 범위에서 유일한지도 확인해야 합니다. 상점마다 같은 번호를 발급한다면 상점 ID와 주문 번호를 함께 써야 합니다. 상품 한 개당 한 행인 테이블을 주문 번호 하나로만 중복 제거하면, 정상적인 주문 상품을 지울 수 있습니다. 한 행이 무엇을 뜻하는지 확인하는 작업이 집계보다 먼저입니다.

매출을 비교하기 전에 어떤 기준을 맞춰야 하나

매출 지표에는 계산식 외에도 포함 범위와 날짜 기준이 필요합니다. 다음은 이 예제에 적용한 명세입니다. 회사의 회계 매출 정의를 대신하는 표가 아니라, 분석 결과를 다시 계산하기 위한 약속입니다.

항목예제의 기준실제 업무에서 달라질 수 있는 부분
분석 단위단일 상점의 주문 1건주문 상품, 결제 승인, 정산 건
날짜결제 시각을 한국 시간으로 환산한 9월 2일주문 생성일, 매체 클릭일, 정산일
통화KRW환산일과 환율, 통화별 소수 단위
중복동일 주문의 동일 값은 한 번만 사용수정 주문, 분할 결제, 재승인
제외테스트 주문직원 주문, 취소 상태, 부정 거래
환불대상 주문에 연결된 환불을 기준일까지 차감환불 발생일 기준 차감
기준일9월 5일 00:00 한국 시간당일 잠정치, 주간 확정치
광고 귀속적용하지 않음귀속 모델, 클릭·조회 인정 기간

원본과 비교할 때는 ‘어느 숫자가 맞는가’보다 ‘같은 질문에 답하는 숫자인가’를 확인하는 편이 빠릅니다. 주문의 결제 금액은 거래 상태를 설명하고, 광고에 귀속한 매출은 설정한 귀속 규칙에 따라 성과를 배분합니다. 후자가 전자와 같아야 할 이유는 집계 조건을 보기 전에는 정할 수 없습니다.

시간대를 맞추면 분석 날짜가 어떻게 달라지나

예제의 o101은 한국 시간으로 9월 2일 00:05에 결제됐습니다. UTC로 저장하면 9월 1일 15:05입니다. 반대로 o105는 UTC로 9월 2일 15:30이지만, 한국에서는 9월 3일 00:30에 해당합니다. 저장된 문자열에서 날짜 앞부분만 잘라 집계하면 한국 시간의 9월 2일 주문과 다른 대상을 셉니다.

하루는 시작 시각을 포함하고 다음 날 시작 시각을 제외하는 구간으로 잡습니다. 9월 2일 00:00 이상, 9월 3일 00:00 미만입니다. 이렇게 하면 정확히 자정에 발생한 주문이 두 날짜에 동시에 들어가지 않습니다.

날짜를 자르기 전에 시간대를 환산합니다주문UTC 저장 시각한국 시간 · 9월 2일 집계 여부o1019월 1일 15:05+9시간 →9월 2일 00:05 · 포함o1059월 2일 15:30+9시간 →9월 3일 00:30 · 제외합성 예제 · 실제 쿼리에서는 명시적인 시간대와 날짜 경계를 사용합니다.
한국 시간에서는 UTC 날짜가 다른 두 로그의 포함 여부가 바뀝니다.

한국 외 지역까지 다룬다면 시간을 고정된 숫자만큼 더하는 구현으로 끝내지 않는 편이 좋습니다. 지역에 따라 일광절약시간이 적용될 수 있으므로 Python에서는 ZoneInfo('Asia/Seoul')처럼 시간대 이름을 사용합니다. 예제도 시간대가 없는 타임스탬프는 임의로 해석하지 않고 오류로 처리합니다.

중복·테스트·환불을 처리하면 매출은 얼마인가

9월 2일 구매 로그 5행의 금액을 더하면 289,900원입니다. o101의 중복 기록 100,000원을 빼면 189,900원, 테스트 주문 9,900원을 제외하면 180,000원입니다. 여기에 o102의 부분 환불 20,000원을 반영하면 160,000원이 남습니다.

같은 주문 데이터에 집계 기준을 차례로 적용한 결과로그 단순 합계289,900원주문 중복 제거189,900원테스트 제외180,000원환불 반영160,000원합성 데이터 · 9월 2일 주문 코호트 · 9월 5일 00:00까지 발생한 환불 반영
막대 길이는 금액에 비례합니다. 각 단계에서 빠진 금액을 설명할 수 있어야 합니다.

o102는 9월 4일에 환불됐습니다. 따라서 9월 3일 00:00을 기준일로 계산하면 순매출은 180,000원이고, 9월 5일 00:00을 기준일로 계산하면 160,000원입니다. 같은 9월 2일 주문을 보더라도 언제까지의 환불을 반영했는지에 따라 결과가 달라집니다.

특정 구매 집단의 가치를 추적할 때는 이처럼 주문 날짜에 묶은 코호트에 환불을 연결할 수 있습니다. 기간별 거래 흐름을 보려면 9월 4일에 환불을 차감하는 정의가 필요합니다. 두 방식은 답하는 질문이 다르므로 표 제목에 ‘주문일 기준’과 ‘환불 반영 기준일’을 함께 적는 것이 좋습니다.

조금 더 일반적으로 쓰면, 날짜 D의 실제 주문 집합을 O(D), 주문 금액을 v(o), 기준 시각 a까지 확인한 해당 주문의 환불 합계를 r(o, a)라고 둘 수 있습니다.

주문 코호트 순매출 R(D, a) = Σ [v(o) − r(o, a)]
                             o ∈ O(D)

O(D)를 만드는 중복 제거·테스트 제외·시간대 규칙이 바뀌면 합산 대상도 달라집니다. 순매출 공식을 공유해도 집합을 만드는 규칙이 다르면 같은 결과가 나오지 않습니다.

GA4와 BigQuery를 비교할 때 어디부터 확인하나

GA4 보고서와 BigQuery 결과를 비교할 때도 지표 이름만 맞추면 부족합니다. Google은 보고 식별 기준, 시간대, 내보내기에서 제외한 스트림과 이벤트를 먼저 확인하도록 안내합니다. 비교하는 데이터 범위와 사용자 구분 방식이 다르면 차이가 생길 수 있습니다. Google Analytics의 보고서·BigQuery 비교 안내

조회 시점도 남겨야 합니다. GA4는 데이터 수집·처리·보고가 같은 순간에 끝나지 않습니다. Google의 데이터 최신성 문서는 처리 중 보고서 수치가 바뀔 수 있다고 설명합니다. 어제 데이터를 오늘 오전에 조회한 결과와 며칠 뒤 다시 조회한 결과를 비교한다면, 새로 처리된 데이터와 귀속 결과 변경을 조사 대상에 포함해야 합니다. GA4 데이터 최신성 안내

운영 리포트에는 분석 기간과 별도로 추출 시각을 남길 수 있습니다. 숫자를 매일 덮어쓰기만 하면 실제 성과가 달라졌는지, 과거 데이터가 보완됐는지 구분하기 어렵습니다. 잠정치와 재집계 결과를 함께 보관하면 차이를 설명할 근거가 남습니다.

MMP 데이터도 주문 로그처럼 합칠 수 있나

MMP에서 받은 데이터는 제공 경로부터 구분해야 합니다. 앱 이벤트의 사용자 단위 로그를 받는 경우와, 개인정보 보호를 위해 집계된 귀속 결과를 받는 경우를 같은 테이블처럼 다루면 안 됩니다.

예를 들어 Adjust의 SKAdNetwork 문서는 Apple이 사용자 수준 데이터 없이 설치 귀속을 처리하며, 네트워크의 포스트백을 받아 집계한다고 설명합니다. SKAdNetwork 데이터는 Adjust가 직접 기록한 데이터와 구분되고, 포스트백에도 지연이 있습니다. 이 설명을 모든 MMP 데이터의 속성으로 확대해서는 안 됩니다. Adjust의 SKAdNetwork 데이터 흐름

집계형 결과에 주문별 식별자가 없다면 임의로 사용자 로그와 일대일 대응시키지 않습니다. 각 데이터의 관측 범위·시간·귀속 규칙을 적은 뒤, 공통으로 비교할 수 있는 집계 수준을 정해야 합니다. 이름이 같은 ‘구매’ 열도 한쪽은 기록된 이벤트, 다른 쪽은 정해진 규칙으로 배분한 성과일 수 있습니다.

그래프를 확인하는 매도비

Python으로 같은 집계를 재현하는 방법

예제는 Python 3.9 이상과 표준 라이브러리만 사용합니다. 입력 JSON에는 구매 로그, 환불 기록, 집계 날짜와 기준 시각이 들어 있습니다. 지표 정의 예제 ZIP에 코드·합성 데이터·검증 파일을 함께 담았습니다.

ZIP을 풀고 예제 폴더 안에서 실행합니다. 운영체제에 IANA 시간대 데이터가 있어야 zoneinfo가 한국 시간을 읽을 수 있습니다.

python3 analysis.py
python3 -m unittest

주요 출력은 다음과 같습니다.

{
  "raw_events": 5,
  "raw_revenue": 289900,
  "unique_orders_including_test": 4,
  "deduplicated_revenue": 189900,
  "orders": 3,
  "purchasers": 2,
  "gross_revenue": 180000,
  "refunds": 20000,
  "net_revenue": 160000,
  "advertising_cost": 40000,
  "blended_mer": 4.0
}

코드는 같은 주문 번호의 금액이나 구매자가 다르면 임의로 큰 값을 선택하지 않고 오류를 냅니다. 실제로 수정된 주문인지, 잘못 연결된 데이터인지 확인해야 하기 때문입니다. 동일한 환불 기록도 환불 ID로 한 번만 차감합니다.

검증에는 표의 합계뿐 아니라 자정 경계, 환불 기준일 변경, 환불 중복, 충돌하는 주문 값, 광고비 0원, 다른 통화, 시간대 없는 입력을 포함했습니다. 입력은 이미 도착한 스냅샷이라고 가정하며, 늦은 수집을 복원하거나 회계상 매출 인식까지 구현한 코드는 아닙니다.

숫자가 맞아도 성과 해석은 따로 확인해야 한다

예제의 전체 순매출 160,000원을 광고비 40,000원으로 나누면 4배, 백분율로는 400%입니다. 여기서는 전체 순매출과 전체 광고비의 비율을 blended_mer로 표시했습니다. 분자에 어떤 매출을 넣었는지 이름과 함께 기록해야 합니다.

이 계산에는 채널별 귀속이 없습니다. 따라서 특정 광고 채널의 ROAS로 사용할 수 없고, 광고가 없었어도 발생했을 구매를 분리하지 않았으므로 광고의 증분 효과도 알 수 없습니다. 수집과 집계가 정확하다는 사실만으로 성과에 대한 인과적 설명이 완성되지는 않습니다.

리포트의 숫자가 어긋났다면 먼저 비교하는 날짜, 집계 단위, 제외 조건, 환불 기준일을 한 줄씩 맞춰 보세요. 그래도 남는 차이를 식별 방식, 귀속 모델, 수집 누락과 지연으로 좁혀 가면 확인할 위치가 구체적으로 잡힙니다.

공식 문서는 2026년 9월 6일 확인했습니다. 제품 설정과 제공 데이터는 변경될 수 있으므로 실제 분석에서는 해당 속성·앱·내보내기의 현재 설정을 함께 기록합니다.

마케팅 데이터 모델·품질·지표 정의 연재 1편입니다. 다음 글: 구매 이벤트 중복을 제거할 때 transaction_id부터 확인하는 이유

🐹

자주 묻는 질문

GA4 구매 이벤트 수와 주문 수가 다른 이유는 무엇인가요?

이벤트는 기록된 행동이고 주문은 거래입니다. 같은 주문에서 구매 이벤트가 여러 번 기록되거나 테스트 결제가 포함되면 이벤트 행 수와 실제 주문 수가 달라질 수 있습니다. 어떤 지표를 비교하는지 확인하고, 주문 식별자와 제외 조건을 맞춘 뒤 차이를 조사해야 합니다.

GA4와 BigQuery의 숫자는 반드시 일치해야 하나요?

항상 일치하는 것은 아닙니다. Google은 보고 식별 기준, 시간대, 내보내기에서 제외한 이벤트·스트림 등을 먼저 확인하도록 안내합니다. 비교하는 보고서의 지표와 집계 범위, 조회 시점도 함께 고정해야 합니다.

환불은 구매일과 환불일 중 어느 날짜에서 빼야 하나요?

분석 목적에 따라 다릅니다. 특정 날짜에 구매한 주문의 최종 가치를 보려면 그 주문에 연결된 환불을 기준일까지 반영합니다. 기간 중 발생한 매출과 환불 흐름을 보려면 환불 발생일에 차감할 수 있습니다. 두 정의를 같은 순매출이라는 이름으로 섞지 않는 것이 중요합니다.

전체 매출을 광고비로 나누면 채널 ROAS가 되나요?

채널별 광고 귀속 매출을 구하지 않았다면 채널 ROAS로 해석할 수 없습니다. 전체 매출과 전체 광고비의 비율을 사용할 때는 매출 범위와 총액·순액 기준을 명시해야 합니다. 이 비율만으로 광고가 추가로 만든 매출을 알 수는 없습니다.