5살도 이해하는 설계 문서

한 장의 행동 쪽지로
모든 분석 도구에 알리기

화면마다 GTM, Airbridge, 검색 로그, EAPP Tracker를 따로 부르지 않습니다. 화면은 “무슨 일이 있었는지”만 한 번 말하고, 중앙 트래킹 모듈이 각 시스템의 언어로 번역합니다.

1. 전체 구조

화면
track()
→
이벤트 Catalog
의미·정책 검사
→
정규 이벤트
TrackingEvent
→
Adapter
각 언어로 번역
→
GTM · Airbridge
Search · EAPP
비유: 아이가 엄마에게 “오더픽에서 주문을 완료했어”라고 한 번 말합니다. 엄마가 할머니에게는 한국어, 외국인 친구에게는 영어로 각각 알려줍니다. 아이가 모든 언어를 알 필요는 없습니다.

2. TrackingEvent의 각 열은 왜 필요한가?

domain

어느 큰 가게의 일인가?

orderpick · wine · search

이벤트를 소유하고 유지하는 bounded context.

area

가게 안 어느 코너인가?

commerce · discovery · promotion

분석 목적. domain과 값이 겹치지 않습니다.

objectType / objectId

무엇에 한 행동인가?

order / ORDER-123

type은 대상 종류, id는 실제 식별자가 있을 때만 사용.

action

무엇을 했는가?

view · cancel · complete

UI click이 아니라 의미 중심 행동. 입력 방식은 interaction으로 분리.

scope

얼마나 넓게 적용됐나?

single · partial · full

상품 하나, 주문 일부, 주문 전체를 구분.

surface / placement

어디서 일어났나?

order_detail / order_actions

surface는 화면, placement는 화면 내부 구좌.

interaction

어떤 경로로 조작했나?

control + confirm

버튼, 뒤로가기, 닫기, gesture, system을 구분.

outcome

실제로 어떻게 끝났나?

attempted · succeeded · rejected

클릭과 성공을 섞지 않게 해 줍니다.

3. 가장 중요한 결정: 클릭·의도·결과를 섞지 않는다

상황actioninteractionoutcome
주문 취소 버튼 실행cancelcontrol / primaryattempted
로그인이 필요해 차단cancelcontrolrejected / authentication_required
서버에서 취소 완료cancel생략 가능succeeded
취소 화면에서 뒤로가기exitback_navigationabandoned

구매 버튼을 눌렀다는 사실만으로 checkout succeeded를 만들지 않습니다. 미로그인·재고 부족·옵션 미선택일 수 있기 때문입니다.

4. Catalog는 이벤트 사전이자 교통경찰

'orderpick.commerce.order.complete': {
  confirmedBy: 'backend',
  outcomePolicy: 'required',
  defaultScope: 'single',
  destinations: ['gtm', 'airbridge'],
  owner: 'orderpick',
  lifecycle: 'active',
  schemaVersion: 1
}

confirmedBy

누구의 확인을 믿을지 결정합니다. 버튼 클릭은 interaction, viewport는 client, 주문 완료는 backend가 확인합니다.

destinations

화면은 전송 대상을 모릅니다. Catalog가 GTM·Airbridge·Search·EAPP 중 어디로 보낼지 결정합니다.

lifecycle

이벤트를 바로 삭제하지 않고 draft → active → deprecated → retired로 관리합니다.

5. 여러 웹뷰를 건너는 Flow

상품 화면
startFlow
→
결제 웹뷰
resumeFlow
→
중간 이벤트
sequence +1
→
주문 완료
flow.end

localStorage에는 flowId, eventKey, sequence, 시작·만료 시각, 버전만 저장합니다. 주문번호·상품·검색어·사용자 식별자는 저장하지 않습니다. TTL은 30분이며, 완료하면 즉시 삭제합니다.

6. Adapter 활용 예시

dataLayer.push({
  event: 'orderpick_commerce_order_complete',
  surface: 'order_complete',
  outcome: 'succeeded'
})
airbridge.ecommerce.order.completed
// products, transactionID, paymentName으로 변환
{
  event_source: 'app-dg-apple-recomm-form-select',
  event_type: 'click',
  event: { screen_id, check_list },
  // user_id 없음
}

7. 개인정보와 안전 규칙

APP_CID 금지

canonical event와 EAPP adapter payload에 넣지 않습니다. 키와 값 모두 검사합니다.

원본 응답 금지

API response, stack trace, 서버 메시지, 사용자가 직접 쓴 문장을 통째로 attributes에 넣지 않습니다.

제품 기능 비차단

분석 시스템이 실패해도 결제·이동·취소가 멈추면 안 됩니다. Adapter는 독립 비동기 실행합니다.

Clean cutover

canonical 호출을 추가하면 같은 변경에서 기존 직접 호출을 제거합니다. 이중 집계를 허용하지 않습니다.

8. 잘 쓰는 예와 피해야 할 예

좋은 예

tracking.track(
  'orderpick.commerce.order.cancel',
  {
    objectId: orderId,
    scope: 'full',
    outcome: {
      status: 'succeeded',
      nextState: 'cancelled'
    }
  }
)

피해야 할 예

tracking.track('cancel-click', {
  objectId: 'purchase',
  attributes: {
    APP_CID,
    rawResponse,
    errorMessage
  }
})

9. 운영 체크리스트

새 이벤트 추가key, 입력 타입, catalog 정책, adapter mapping, 테스트를 함께 추가
성공 이벤트backend 응답으로 실제 상태 전환을 확인한 지점에서만 발행
노출 이벤트렌더링이 아니라 viewport 기준과 중복 정책을 명확히 정의
페이지뷰Router에서 한 번 발행하고 GTM 자동 page view와 중복 금지
검증lint, 신규 vue-tsc 오류 0, 관련 Jest, build, 실제 payload smoke

핵심 한 문장: 화면은 “무슨 일이 있었는지”만 말하고, Catalog와 Adapter가 “누구에게 어떤 모양으로 알릴지” 책임집니다.