# 이벤트 이름과 속성을 관리하는 트래킹 명세

> 이벤트 이름·발생 조건·필수 속성·담당자를 하나의 명세로 관리합니다. GA4 규칙과 내부 계약을 구분하고, Python으로 문서와 검증 결과를 함께 생성하며 스키마 변경의 호환성을 확인합니다.

- 발행: 2026-09-07
- 주제: 마케팅 데이터 분석
- 발행처: 매드업 마케팅사업본부 AI 마케팅 개발실
- 웹 원문: https://madobi.madup.com/blog/tracking-plan-event-names-properties/

## 세 줄 요약

- 트래킹 명세에는 이벤트 이름뿐 아니라 무엇이 실제로 일어났을 때 보내는지, 어떤 속성이 필요한지, 누가 관리하는지를 함께 기록한다.
- 합성 이벤트 11개를 내부 명세로 검사하면 정상 3개와 오류 8개가 구분된다. 문서와 검증기가 같은 contract.json을 읽어 이름·타입·금액 규칙의 불일치를 줄인다.
- 형식이 맞아도 실제 구매가 발생했거나 전송자가 인증됐다고 보장할 수는 없다. 선택 속성 추가도 기존 수신기의 허용 정책에 따라 호환성이 달라진다.

---
장바구니 보고서에는 `add_to_cart`가 있고 개발 로그에는 `AddToCart`가 있습니다.
구매 보고서의 `value`는 숫자인데 일부 이벤트에는 `"100000"`이라는 문자열이
들어옵니다. 이름을 맞추고 문자열을 숫자로 바꾸면 일단 집계는 됩니다. 그 뒤에도
결제 버튼 클릭이 구매로 기록돼 있었다면 전환율은 여전히 잘못 해석될 수 있습니다.

트래킹 명세는 이런 차이를 구현 전에 드러내는 문서입니다. 이벤트 이름, 발생 조건,
속성의 의미와 제약을 합의하고 실제 코드가 그 합의를 지키는지 확인할 수 있어야 합니다.
문서만 고치거나 코드만 고치면 어느 쪽을 기준으로 보고서를 읽을지 다시 물어야 합니다.

예제는 상품 조회·장바구니 추가·구매를 다루는 합성 커머스 명세입니다. 실제 고객
자료나 운영 로그를 사용하지 않으며, GA4에 데이터를 보내지 않습니다.

## 이벤트 이름보다 먼저 발생 조건을 정합니다

`purchase`라는 이름이 같아도 전송 시점이 다르면 서로 다른 행동을 세게 됩니다.
결제 요청을 보낸 순간, 결제 완료 응답을 받은 순간, 완료 화면을 다시 연 순간을
모두 구매로 기록하면 하나의 주문이 여러 번 나타날 수도 있습니다.

Google의 권장 이벤트에서 `add_to_cart`는 장바구니에 상품을 추가한 행동을,
`purchase`는 상품을 구매한 행동을 뜻합니다. 내부 구현에서는 이 의미를 어떤
상태 변화로 확인할지 구체화해야 합니다.
[GA4 권장 이벤트 정의](https://developers.google.com/analytics/devguides/collection/ga4/reference/events)

<figure style="overflow-x:auto">
<svg viewBox="0 0 720 292" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="합성 결제 흐름에서 버튼 클릭은 구매 의도, 결제 처리 중은 결과 미확정, 백엔드 결제 완료 확인은 예제의 purchase 발생 조건입니다. 완료 화면 재조회는 새 주문이 생겼다는 근거가 아닙니다." style="width:100%; min-width:640px; height:auto; font-family:inherit">
  <rect width="720" height="292" rx="16" fill="#fffdf8"/>
  <text x="24" y="32" fill="#2a2d3e" font-size="16" font-weight="700">합성 결제 흐름에서 purchase를 보내는 시점</text>
  <rect x="24" y="58" width="204" height="116" rx="12" fill="#eef0f7"/>
  <text x="42" y="90" fill="#3e4a78" font-size="16" font-weight="700">결제 버튼 클릭</text>
  <text x="42" y="124" fill="#2a2d3e" font-size="14">결제를 시도한 행동</text>
  <text x="42" y="153" fill="#5f6273" font-size="13">완료 여부는 아직 모릅니다</text>
  <text x="239" y="122" fill="#3e4a78" font-size="20">→</text>
  <rect x="266" y="58" width="188" height="116" rx="12" fill="#fff1cd"/>
  <text x="284" y="90" fill="#684d13" font-size="16" font-weight="700">결제 처리 중</text>
  <text x="284" y="124" fill="#2a2d3e" font-size="14">성공·실패 미확정</text>
  <text x="284" y="153" fill="#5f6273" font-size="13">purchase 조건 미충족</text>
  <text x="465" y="122" fill="#3e4a78" font-size="20">→</text>
  <rect x="492" y="58" width="204" height="116" rx="12" fill="#fcebee"/>
  <text x="510" y="90" fill="#a52b35" font-size="16" font-weight="700">백엔드 완료 확인</text>
  <text x="510" y="124" fill="#2a2d3e" font-size="14">주문·결제 상태 확인</text>
  <text x="510" y="153" fill="#a52b35" font-size="13">예제의 purchase 조건</text>
  <rect x="24" y="196" width="672" height="64" rx="10" fill="#eef0f7"/>
  <text x="42" y="222" fill="#2a2d3e" font-size="14">완료 화면 재조회 → 새 주문 발생으로 세지 않습니다.</text>
  <text x="42" y="246" fill="#5f6273" font-size="13">전송 재시도와 중복 제거는 주문 식별자를 기준으로 별도 설계합니다.</text>
  <text x="24" y="280" fill="#5f6273" font-size="12">업무 의미를 정한 도식 · 아래 형식 검증기만으로 이 흐름을 확인할 수는 없습니다.</text>
</svg>
<figcaption>이벤트의 발생 조건을 코드에서 관찰할 수 있는 상태와 연결합니다.</figcaption>
</figure>

이 명세의 구매 생산자는 `checkout_service`로 정합니다. 백엔드에서 확인한 주문
상태를 기준으로 구매를 만들겠다는 내부 설계 선택입니다. 모든 GA4 구현에서 구매를
서버로 보내야 한다는 규칙은 아닙니다. 실제 전송 방식과 재시도 처리는 별도 구현이
필요하며, 브라우저에서 동일 구매를 함께 보내는 경우의 중복 처리도 확인해야 합니다.

## 담당자와 속성의 의미까지 한 표에 기록합니다

작은 명세도 마케터와 개발자가 같은 질문에 답할 수 있어야 합니다. 어떤 분석에
필요한 이벤트인지, 어느 서비스가 만들고 누가 변경을 검토하는지 함께 적습니다.

| 이벤트 | 분석 목적 | 발생 조건 | 생산자 / 담당 |
| --- | --- | --- | --- |
| `view_item` | 상품 상세 관심 행동 | 상품 상세 정보가 화면에 정상 표시됐을 때 | `web_storefront` / `storefront` |
| `add_to_cart` | 장바구니 추가 행동 | 추가 성공 후 이번에 추가된 상품·수량 기준 | `web_storefront` / `cart` |
| `purchase` | 결제 완료 구매 측정 | 백엔드에서 결제 완료가 확인된 주문 기준 | `checkout_service` / `checkout` |

`add_to_cart`의 `items`에는 이번에 추가된 상품과 수량을 넣습니다. 장바구니 전체를
넣는 것으로 바꾸면 반복 추가 시 같은 상품 금액이 다시 집계될 수 있습니다. 속성명이
같다는 이유만으로 의미도 같다고 볼 수 없습니다.

세 이벤트 모두 `currency`, `value`, `items`를 필수로 두고 구매에는
`transaction_id`를 추가합니다. `coupon`은 구매에서만 선택 속성으로 허용합니다.
상품 행에는 `item_id`, `price`, `quantity`가 모두 있어야 합니다. 이는 이 예제에서
채택한 내부 필수 조건입니다.

운영 명세에는 이 표 외에도 발생 시각의 기준, 식별자 범위, 중복 제거 키, 보존 기간과
민감정보 취급, 사용하는 리포트를 연결할 수 있습니다. 모든 이벤트에 같은 속성을
복제하기보다 분석 목적과 수집 필요성을 검토해 추가합니다.

## GA4 규칙과 팀 내부 규칙을 구분합니다

GA4는 이벤트 이름의 대소문자를 구분하며 영어와 비영어 이름을 허용합니다.
이름은 문자로 시작하고 문자·숫자·밑줄을 사용하며 공백은 허용하지 않습니다.
예약 이름과 접두사도 별도로 확인해야 합니다.
[GA4 이벤트 이름 규칙](https://support.google.com/analytics/answer/13316687?hl=en)

예제에서는 이름을 소문자 ASCII 문자로 시작하는 최대 40자의 snake_case로
제한합니다. `snake_case`는 여러 단어를 밑줄로 잇는 표기입니다. 내부 리뷰와 검색을
단순하게 하려는 선택이며, GA4가 영어 소문자만 받는다는 뜻은 아닙니다.

```text
^[a-z][a-z0-9_]{0,39}$
```

이 정규식에 맞아도 명세에 없는 이벤트는 통과시키지 않습니다. 예를 들어
`puchase`는 문자 규칙에는 맞지만 등록한 `purchase`와 다른 이름입니다.
대소문자를 강제로 바꾸거나 오타를 추정해 고치면 어디서 잘못 만들었는지 숨길 수
있으므로, 검증기는 문제를 보고하고 원문을 유지합니다.

## 금액 속성에는 타입과 계산식을 함께 적습니다

`value`가 숫자라는 설명만으로는 부족합니다. 원인지 달러인지, 주문 전체인지 상품
합계인지, 배송비와 세금이 포함됐는지 정해야 비교할 수 있습니다. GA4 권장 구매
이벤트 문서는 `value`를 상품별 가격과 수량의 곱을 합한 금액으로 안내하고 배송비와
세금은 포함하지 않도록 설명합니다. `value`를 설정할 때의 `currency` 조건도 확인해야
합니다.
[GA4 purchase 속성](https://developers.google.com/analytics/devguides/collection/ga4/reference/events#purchase)

합성 명세는 KRW만 사용하며 상품 가격과 수량을 정수로 제한합니다. 가격은 0 이상,
수량은 1 이상이고 상품 목록은 비어 있으면 안 됩니다. 여기서 가격은 이 예제의 상품
금액 계산에 사용하는 단가입니다. 배송비·세금·환불·할인 배분은 입력에 넣지 않습니다.
실제 운영에서는 주문 원장의 금액 정의와 이 단가를 연결하는 규칙이 필요합니다.

| 속성 | 내부 타입·범위 | 의미 |
| --- | --- | --- |
| `currency` | 문자열, `KRW`만 허용 | 예제 금액의 통화 |
| `value` | 정수, 0 이상 | 해당 이벤트 상품 금액 합계 |
| `items` | 비어 있지 않은 배열 | 해당 행동에 포함된 상품 행 |
| `items[].price` | 정수, 0 이상 | 상품 단가 |
| `items[].quantity` | 정수, 1 이상 | 해당 상품 수량 |
| `transaction_id` | 공백만으로 이뤄지지 않은 문자열 | 구매 주문 식별자 |

정수 KRW는 계산을 간단히 재현하기 위한 내부 제한입니다. 다중 통화나 소수 단가를
쓰는 서비스에는 통화별 정밀도와 반올림 규칙이 더 필요합니다. GA4가 정수 금액만
지원한다는 뜻도 아닙니다.

각 필드의 타입을 확인한 다음에는 필드 사이의 관계를 검사합니다.

```text
value = Σ (items[i].price × items[i].quantity)

합성 구매 예시:
100,000 = 40,000 × 1 + 30,000 × 2
```

`value`가 99,000인 이벤트는 정수 검사에는 통과하지만 합계 검사에서 실패합니다.
타입 검사와 금액 대사는 서로 다른 오류를 찾습니다. 반대로 합계가 맞더라도 주문
원장에 그 구매가 존재한다는 사실까지 증명하지는 않습니다.

## 같은 명세에서 문서와 Python 검증기를 만듭니다

문서에 적힌 필수 속성과 검증기의 필수 속성을 따로 고치면 시간이 지나면서
어긋날 수 있습니다. 예제는 `contract.json`을 함께 읽도록 구성합니다. 사람이 읽는
표는 `generate_plan.py`가 만들고, `analysis.py`는 같은 파일의 규칙으로 이벤트를
검사합니다.

<figure style="overflow-x:auto">
<svg viewBox="0 0 720 294" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="contract.json에 이벤트 정의와 속성 규칙을 기록하면 generate_plan.py가 TRACKING_PLAN.md를 생성하고 analysis.py가 같은 명세로 합성 이벤트를 검증합니다. 업무상 발생 조건은 문서화되지만 실제 발생 확인은 별도의 통합 검증이 필요합니다." style="width:100%; min-width:640px; height:auto; font-family:inherit">
  <rect width="720" height="294" rx="16" fill="#fffdf8"/>
  <text x="24" y="32" fill="#2a2d3e" font-size="16" font-weight="700">정의는 한 곳에서 고치고 문서와 검증에 함께 반영합니다</text>
  <rect x="24" y="66" width="248" height="140" rx="12" fill="#eef0f7"/>
  <text x="44" y="104" fill="#3e4a78" font-size="23" font-weight="700">contract.json</text>
  <text x="44" y="140" fill="#2a2d3e" font-size="14">이름 · 발생 조건 · 담당</text>
  <text x="44" y="170" fill="#2a2d3e" font-size="14">속성 규칙 · 금액 식 · 버전</text>
  <path d="M272 136 H314 V98 H352 M314 136 V188 H352" fill="none" stroke="#3e4a78" stroke-width="2"/>
  <text x="328" y="92" fill="#3e4a78" font-size="18">→</text>
  <text x="328" y="182" fill="#3e4a78" font-size="18">→</text>
  <rect x="358" y="58" width="338" height="76" rx="12" fill="#fff1cd"/>
  <text x="378" y="87" fill="#684d13" font-size="15" font-weight="700">generate_plan.py → 문서 생성</text>
  <text x="378" y="116" fill="#2a2d3e" font-size="14">TRACKING_PLAN.md로 정의 검토</text>
  <rect x="358" y="152" width="338" height="76" rx="12" fill="#fcebee"/>
  <text x="378" y="181" fill="#a52b35" font-size="15" font-weight="700">analysis.py → 이벤트 검사</text>
  <text x="378" y="210" fill="#2a2d3e" font-size="14">타입 · 필수값 · 허용 속성 · 합계</text>
  <text x="24" y="258" fill="#2a2d3e" font-size="14">발생 조건은 문서에 남고, 실제 발생 여부는 통합 검증으로 확인합니다.</text>
  <text x="24" y="282" fill="#5f6273" font-size="12">자체 명세 형식 · 표준 JSON Schema 전체나 GA4 전체 규칙을 구현한 구조는 아닙니다.</text>
</svg>
<figcaption>담당자·목적·발생 조건은 사람이 검토하고, 표현 가능한 필드 제약은 코드로 검사합니다.</figcaption>
</figure>

구매 이벤트의 정의는 다음과 같습니다. 전체 파일에는 각 속성의 타입·범위와 다른
두 이벤트의 정의도 들어 있습니다.

```json
{
  "name": "purchase",
  "producer": "checkout_service",
  "owner": "checkout",
  "purpose": "결제 완료 구매 측정",
  "trigger": "백엔드에서 결제 완료가 확인된 주문 기준",
  "required": ["currency", "value", "items", "transaction_id"],
  "optional": ["coupon"]
}
```

검사 대상은 버전과 생산자 선언을 포함하는 내부 메시지입니다. `properties` 안에
분석용 속성을 넣습니다. 이 바깥 구조를 envelope라고 부르며, 실제 GA4 전송 요청의
구조를 그대로 나타내는 것은 아닙니다.

```json
{
  "schema_version": 1,
  "event_name": "purchase",
  "producer": "checkout_service",
  "properties": {
    "currency": "KRW",
    "value": 100000,
    "transaction_id": "order-001",
    "items": [
      {"item_id": "sku-a", "price": 40000, "quantity": 1},
      {"item_id": "sku-b", "price": 30000, "quantity": 2}
    ]
  }
}
```

이 구조에서는 생산자 선언이 명세와 같은지 확인할 수 있습니다. 누구든
`"producer": "checkout_service"`라고 적을 수 있으므로 문자열 일치가 서비스의
진위를 인증하지는 않습니다. 실제 수집 경로에서는 인증과 권한 검사를 따로 둡니다.

## 합성 이벤트 11개에서 어떤 오류를 찾는가

정상 메시지 3개와 의도적으로 오류를 넣은 메시지 8개를 준비했습니다. 검증기는
다음과 같이 분류합니다.

| 사례 | 결과 | 확인한 문제 |
| --- | --- | --- |
| 정상 조회·장바구니·구매 | 통과 3개 | 명세의 필드 제약과 합계에 부합 |
| `Purchase` | 격리 후보 | 내부 이름 규칙과 등록 이름 불일치 |
| 통화 누락 | 격리 후보 | `properties.currency` 필수값 없음 |
| 빈 주문 ID | 격리 후보 | `properties.transaction_id`가 빈 문자열 |
| 수량 `"2"` | 격리 후보 | 두 번째 상품의 수량이 문자열 |
| 구매 금액 99,000원 | 격리 후보 | 상품 합계 100,000원과 불일치 |
| 미등록 `customer_email` 속성 | 격리 후보 | 허용 속성 목록에 없음 |
| 명세 버전 2 | 격리 후보 | 현재 검사기는 버전 1만 처리 |
| 구매 생산자 `web_storefront` | 격리 후보 | 명세에 선언한 생산자와 다름 |

<figure style="overflow-x:auto">
<svg viewBox="0 0 720 254" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="합성 이벤트 11개를 내부 명세로 검사한 결과 정상 3개와 격리 후보 8개로 구분됩니다. 격리 후보는 이름, 필수값, 타입, 합계, 허용 속성, 버전, 생산자 선언 오류를 포함하며 실제 전송을 차단한 결과는 아닙니다." style="width:100%; min-width:640px; height:auto; font-family:inherit">
  <rect width="720" height="254" rx="16" fill="#fffdf8"/>
  <text x="24" y="32" fill="#2a2d3e" font-size="16" font-weight="700">합성 메시지 11개를 내부 명세로 검사한 결과</text>
  <rect x="24" y="56" width="252" height="130" rx="12" fill="#eef0f7"/>
  <text x="44" y="87" fill="#3e4a78" font-size="14">accepted · 정상</text>
  <text x="44" y="134" fill="#2a2d3e" font-size="32" font-weight="700">3개</text>
  <text x="44" y="167" fill="#5f6273" font-size="13">조회 · 장바구니 · 구매</text>
  <rect x="296" y="56" width="400" height="130" rx="12" fill="#fcebee"/>
  <text x="316" y="87" fill="#a52b35" font-size="14">quarantine · 격리 후보</text>
  <text x="316" y="134" fill="#a52b35" font-size="32" font-weight="700">8개</text>
  <text x="394" y="126" fill="#2a2d3e" font-size="13">이름 · 누락 · 빈 값 · 타입</text>
  <text x="394" y="151" fill="#2a2d3e" font-size="13">합계 · 미등록 속성 · 버전 · 생산자</text>
  <text x="24" y="217" fill="#2a2d3e" font-size="14">오류 경로와 코드를 남기며 메시지를 임의로 고치지 않습니다.</text>
  <text x="24" y="242" fill="#5f6273" font-size="12">오프라인 합성 예제 · 운영 시스템의 오류율이나 실제 전송 차단 건수가 아닙니다.</text>
</svg>
<figcaption>8개라는 결과는 설계한 오류 사례의 개수이며, 실제 서비스의 품질 추정치가 아닙니다.</figcaption>
</figure>

`customer_email`의 값은 개인정보가 아닌 `redacted-placeholder`라는 합성
문자열입니다. 검증기는 그 내용을 이해한 것이 아니라 속성 이름이 명세 밖에 있다는
이유로 표시했습니다. 허용된 `coupon` 안에 민감한 값이 들어오는 경우까지 탐지하는
개인정보 검사기로 해석할 수는 없습니다.

오류 결과는 원문 값 대신 경로·코드·메시지를 반환합니다. 수량 오류라면
`properties.items[1].quantity`를 가리키므로 수정할 필드를 찾을 수 있습니다.
원문을 보관하거나 운영 로그에 남기는 정책은 이 오프라인 예제의 범위 밖입니다.

## 선택 속성을 추가할 때도 호환성을 확인합니다

기존 구매 명세에 선택 속성을 하나 추가하면 예전 메시지는 그 속성이 없어도 새
수신기에서 읽힐 수 있습니다. 반대 방향은 따로 확인해야 합니다. 기존 수신기가
모르는 속성을 거부한다면 새 속성이 포함된 메시지를 받지 못합니다.

허용하는 메시지의 집합을 `A_old`, `A_new`로 적으면 조건을 더 명확하게 볼 수 있습니다.
여기서는 버전 식별자가 라우팅된 뒤의 속성 규칙만 비교한다고 가정합니다.

```text
기존 형식을 새 수신기가 모두 읽음: A_old ⊆ A_new
새 형식을 기존 수신기가 모두 읽음: A_new ⊆ A_old
```

선택 속성을 허용하면 메시지 집합이 커질 수 있으므로 첫 번째 조건은 만족해도
두 번째 조건은 실패할 수 있습니다. 두 조건을 구분하지 않고 “하위 호환”이라고만
적으면 배포 순서를 결정하기 어렵습니다.

예제는 버전이 다르면 먼저 거부하는 더 단순한 정책을 사용합니다. 버전 2로 바꾼
메시지는 내용이 같아도 버전 1 검사기를 통과하지 않습니다. 운영에서는 여러 버전을
처리하는 수신기를 먼저 배포하거나 변환 계층을 두는 등 실제 전환 경로를 정해야 합니다.
필드 이름을 그대로 둔 채 금액 의미를 바꾸는 변경도 분석에는 큰 영향을 줍니다.
타입 검사가 통과한다는 이유로 안전한 변경으로 분류해서는 안 됩니다.

## 직접 생성하고 검증하는 방법

[트래킹 명세와 Python 검증 예제 ZIP](/downloads/marketing-metrics-11.zip)을 풀고
`marketing-metrics-11` 폴더에서 실행합니다. Python 3.9 이상과 표준 라이브러리만
사용합니다.

```sh
python3 generate_plan.py
python3 analysis.py
python3 -m unittest
```

첫 명령은 `TRACKING_PLAN.md`를 다시 만듭니다. 두 번째 명령은 정상 3개와 격리
후보 8개, 각 사례의 오류 경로를 출력합니다. `quarantine`은 예제의 분류 이름이며
실제 전송을 차단하거나 별도 저장소로 이동시키는 기능은 구현하지 않았습니다.

테스트는 대소문자, 필수값, 숫자로 보이는 문자열과 불리언 수량, 빈 상품 목록,
상품 내부의 미등록 속성, 금액 합계, 선택 쿠폰, 버전·생산자 선언을 확인합니다.
입력을 임의로 고치지 않는지도 검사합니다. 이 코드는 학습용 자체 명세 형식이며
표준 JSON Schema나 GA4의 예약 이름·수집 한도 전체를 구현한 검증기가 아닙니다.

## 형식 검사 다음에는 실제 발생과 수신을 대조합니다

명세대로 생긴 메시지를 보냈다는 사실과 결제가 실제로 완료됐다는 사실은 다릅니다.
결제 실패에는 구매 이벤트가 없는지, 성공 시 주문 ID가 원장과 같은지, 새로고침과
재시도가 중복 구매를 만들지 않는지 실제 동작으로 확인해야 합니다.

웹의 gtag.js 구현에서는 DebugView로 수신된 이벤트와 이벤트·상품 단위 속성을
확인할 수 있습니다. Google도 전자상거래 수집 검증에 이 경로를 안내합니다.
[GA4 전자상거래 수집 검증](https://developers.google.com/analytics/devguides/collection/ga4/validate-ecommerce)

수신 확인 뒤에도 분석 테이블과 보고서에 기대한 의미로 반영됐는지 대조합니다.
명세의 발생 조건, 수집된 메시지, 최종 집계값을 한 주문으로 따라가면 어느 단계에서
의미가 달라졌는지 좁힐 수 있습니다. 문서를 고치는 작업도 그 경로의 검증 결과와
함께 남겨야 다음 변경을 검토할 수 있습니다.

<img src="/madobi/analysis.png" alt="이벤트 명세와 검증 결과를 대조하는 매도비" width="112" height="112" loading="lazy" style="display:block; margin:1.5rem auto; width:112px; height:112px" />

공식 문서는 2026년 9월 7일 확인했습니다. 명세와 메시지, 검사 결과는 설명용 합성
예제이며 실제 고객의 수집 환경을 검증한 결과가 아닙니다.

마케팅 데이터 모델·품질·지표 정의 연재 11편입니다.
[이전 글: 관측 매출과 광고에 귀속된 매출을 나눠 보는 방법](/blog/observed-attributed-incremental-revenue/)

🐹