데이터 트래킹: 사용자가 애플리케이션에서 무엇을 했는지 기록하기
🎯 이 장에서 다룰 문제
사용자가 애플리케이션 안에서 무엇을 했는지 어떻게 알 수 있을까?
오프라인 카페를 운영한다고 상상해 보세요. 카운터 뒤에 서서 고객 한 명 한 명을 직접 관찰할 수 있습니다. 그들이 들어와서 메뉴를 얼마나 오래 보았는지, 어떤 음료를 주문했는지, 망설이다가 나갔는지까지요.
하지만 "매장"이 모바일 앱이나 웹사이트라면 사용자의 조작을 직접 볼 수 없습니다. 이때 애플리케이션의 핵심 위치에 기록 포인트를 "매립"하여 사용자의 모든 조작을 자동으로 기록하는 기술적 수단이 필요합니다. 이것이 바로 데이터 트래킹(Event Tracking)입니다.
"트래킹"이라는 말은 전문적으로 들리지만, 핵심 아이디어는 매우 간단합니다. 사용자가 조작할 수 있는 곳에 "기록 장치"를 두고, 사용자가 무엇을 했는지 기록하는 것입니다.
이 장에서는 다음 네 단계로 설명합니다:
- 수집 방안 선택 — 어디에 기록 장치를 둘지, 어떻게 둘지 결정
- 데이터 형식 설계 — 각 기록에 어떤 정보를 포함해야 하는지 결정
- 전송과 캐싱 — 사용자의 휴대폰에서 서버로 안전하게 기록 전송
- 정제와 적재 — 데이터를 정리하고, 중복과 오류를 제거하여 데이터베이스에 저장
1단계: 수집 방안 선택 — 어디에 기록 장치를 둘 것인가?
목표: 사용자의 조작을 어떤 방식으로 기록할지 결정합니다.
예를 들어: 제품 매니저가 "구매 버튼을 클릭한 사용자가 몇 명인지" 알고 싶어 합니다. 이 질문에 답하려면 개발자가 "구매 버튼" 코드에 기록 로직을 추가해야 합니다 — 사용자가 이 버튼을 클릭할 때마다 자동으로 한 건을 기록하는 것입니다.
하지만 여기에 선택의 문제가 있습니다. 중요한 곳에만 기록 장치를 둘 것인가(예: "구매"와 "가입"만 기록), 아니면 모든 곳에 기록 장치를 둘 것인가(사용자의 모든 클릭, 스와이프, 체류 시간 기록)?
선택에 따라 서로 다른 트래킹 방안이 적용됩니다.
| 捕获到的信息 | 代码埋点 | 可视化埋点 | 全埋点 |
|---|---|---|---|
| 点击了哪个按钮 | ✔ | ✔ | ✔ |
| 点击发生的时间 | ✔ | ✔ | ✔ |
| 用户停留了多久 | ✘ | ✘ | ✔ |
| 商品名称 / 价格 | ✔ | ✘ | ✘ |
| 用了哪张优惠券 | ✔ | ✘ | ✘ |
| 账户余额 | ✔ | ✘ | ✘ |
| 页面滑动轨迹 | ✘ | ✘ | ✔ |
💡 세 가지 주요 트래킹 방식
업계에서 일반적으로 사용하는 트래킹 방안은 세 가지이며, 각각 장단점이 있습니다:
방식 1: 코드 트래킹(Code Tracking) — 수동 정밀 기록
개발자가 코드에서 수동으로 지정합니다: 사용자가 특정 조작을 수행할 때 한 건의 데이터를 기록합니다.
비유하자면: 카페 계산대에 전담 직원을 배치해 "누가 무엇을 샀는지, 얼마를 지불했는지"만 기록하는 것과 같습니다. 기록 정보가 매우 상세하고 정확합니다.
- 장점: 매우 상세한 비즈니스 정보를 기록할 수 있습니다 (예: 사용자가 어떤 쿠폰을 사용했는지, 계정 잔액은 얼마인지)
- 단점: 새로운 기록 포인트를 추가하려면 개발자가 코드를 작성하고, 테스트하고, 새 버전을 배포해야 하므로 프로세스가 깁니다
방식 2: 시각적 트래킹(Visual Tracking) — 클릭 선택 기록
코드를 작성할 필요가 없습니다. 시스템이 시각화 도구를 제공하여, 운영 담당자가 애플리케이션 인터페이스에서 모니터링할 버튼이나 영역을 "선택"하면 시스템이 자동으로 기록을 시작합니다.
비유하자면: 카페의 CCTV 화면에서 마우스로 "계산대 영역"을 선택하면 시스템이 자동으로 해당 구역의 유동 인구를 집계하기 시작하는 것과 같습니다.
- 장점: 개발자의 참여 없이 운영 담당자가 직접 구성할 수 있어 효율이 높습니다
- 단점: "사용자가 무엇을 클릭했는지" 같은 인터페이스 조작만 기록할 수 있고, "주문 금액" 등 심층 비즈니스 데이터는 기록할 수 없습니다
방식 3: 전면 트래킹(Auto Tracking) — 모든 것을 자동 기록
애플리케이션에 SDK(일종의 "도구 패키지")를 통합하면, 사용자의 모든 조작을 자동으로 기록합니다: 모든 클릭, 모든 스와이프, 각 페이지에서의 체류 시간까지요.
비유하자면: 카페의 모든 구석에 CCTV를 설치하여 고객의 일거수일투족을 기록하는 것과 같습니다.
- 장점: 어떤 조작도 누락하지 않아 커버리지가 가장 포괄적입니다
- 단점: 데이터량이 매우 많으며, 그중 많은 부분이 무용한 정보(예: 사용자의 무의식적 스와이프)라서 후속 필터링과 정제에 많은 노력이 필요합니다
이 단계 요약: 트래킹 방식을 선택하면 애플리케이션에 "사용자 조작 기록" 능력이 생깁니다.
하지만 새로운 문제가 있습니다: 기록 장치가 사용자의 조작을 포착할 수 있지만, 각 기록 장치가 기록하는 형식이 다르다면(예: 어떤 것은 "사용자ID"라고 쓰고, 어떤 것은 "userID"라고 쓰고, 어떤 것은 아예 기록하지 않음) 나중에 통합 분석이 불가능합니다. 그래서 다음 단계에서는 통일된 기록 형식을 규정해야 합니다.
2단계: 데이터 형식 설계 — 각 기록에는 무엇이 포함되어야 하는가?
전제 조건: 트래킹 방식을 선택했고(예: 코드 트래킹), 애플리케이션이 사용자의 조작을 포착할 수 있습니다.
이 단계 목표: 통일된 "기록 템플릿"을 규정하여 모든 트래킹 기록의 형식이 일관되게 합니다.
왜 통일된 형식이 필요한가? 카페에 직원 3명이 동시에 판매 상황을 기록하는데, 한 명은 "홍길동이 버블티 5,000원 구매", 다른 한 명은 "5,000, 티, 버블", 세 번째는 "버블티 1잔"이라고 적는다고 상상해 보세요. 월말 집계 시 이 기록들은 형식이 완전히 달라 정리하기가 매우 고통스럽습니다. 그래서 각 기록에 반드시 작성해야 할 항목을 규정하는 통일된 "기록표"가 필요합니다.
"event": "add_to_cart""user_id": "u_98765""time": "2025-08-12T10:33:09Z""device": "iPhone 15", "network": "5G""product": "新款手机", "price": 2999💡 핵심 원리: 4W1H 기록 템플릿
어떤 조작을 기록하든 각 데이터는 다음 다섯 가지 질문에 답해야 합니다(약칭 4W1H):
Who — 누가 했는가?
이 기록이 어느 사용자에 의해 생성되었는지 알아야 합니다.
- 사용자가 이미 로그인한 상태라면 계정 ID를 사용합니다 (예:
user_id: "hong123") - 로그인하지 않은 상태라면 기기의 고유 식별자(예: 휴대폰 기기 번호)를 사용합니다. 적어도 "같은 휴대폰에서의 조작"은 구분할 수 있습니다
When — 언제 했는가?
조작이 발생한 정확한 시간을 밀리초 단위로 기록합니다.
여기에 한 가지 세부 사항이 있습니다: 해외 사용자가 있는 애플리케이션의 경우 서울 시간 오후 3시와 뉴욕 시간 오후 3시는 실제로 13시간 차이가 납니다. 혼란을 피하기 위해 모든 시간을 UTC 표준 시간(즉, "세계 통일 시간")으로 변환합니다.
Where & How — 어떤 환경에서 했는가?
이 부분은 사용자가 조작할 때의 기기 및 네트워크 환경을 기록하며, 공통 속성이라고 부릅니다. "공통"이라고 하는 이유는 사용자가 어떤 조작을 하든 이 정보가 자동으로 첨부되기 때문입니다. 예를 들어:
- 기기 모델: iPhone 15 / 갤럭시 S24
- 네트워크 유형: WiFi / 5G / 4G
- 앱 버전 번호: v1.2.3
- 운영 체제: iOS 18 / Android 15
이 정보의 가치는 특정 기기에서만 나타나는 버그를 발견했을 때 공통 속성이 문제를 빠르게 파악하는 데 도움이 된다는 것입니다.
What — 구체적으로 무엇을 했는가?
이 부분은 조작의 구체적 비즈니스 세부 사항을 기록하며, 커스텀 속성이라고 부릅니다. 조작마다 다른 정보를 기록해야 합니다. 예를 들어:
- 사용자가 "장바구니에 추가" 클릭: 상품명, 상품 가격, 수량 기록 필요
- 사용자가 결제 완료: 주문 금액, 결제 수단, 쿠폰 번호 기록 필요
이 단계 요약: 4W1H 템플릿을 통해 사용자의 모든 조작을 형식이 통일된 데이터 기록으로 변환합니다. 기술적 구현에서 이 기록은 보통 JSON 형식으로 저장됩니다(JSON은 범용 데이터 형식으로, 위의 인터랙티브 컴포넌트에서 그 모습을 확인할 수 있습니다).
하지만 또 다른 문제가 있습니다: 데이터 형식은 통일되었지만, 사용자 수가 많은 경우(예: 프로모션 기간에는 초당 수만 건의 기록이 생성됨) 휴대폰에서 기록이 생길 때마다 즉시 전송하는 것은 불가능합니다 — 배터리와 데이터를 많이 소모하고 서버도 감당하지 못합니다. 그래서 다음 단계에서는 더 똑똑한 전송 방식을 설계해야 합니다.
3단계: 전송과 캐싱 — 어떻게 데이터를 안전하게 서버로 보낼 것인가?
전제 조건: 사용자의 모든 조작이 형식이 통일된 JSON 데이터로 기록되었습니다.
이 단계 목표: 이 데이터를 사용자의 휴대폰(또는 브라우저)에서 서버로 안정적으로 전송합니다. 네트워크 상태가 좋지 않은 경우에도 데이터를 잃지 않습니다.
왜 바로 전송할 수 없는가? 기록이 생성될 때마다 즉시 네트워크 요청을 보내는 것은 편지를 쓸 때마다 우체국에 달려가는 것과 같습니다 — 비효율적입니다. 더 합리적인 방법은 편지를 모아 한 번에 보내는 것입니다.
💡 핵심 원리: 데이터 전송의 3중 보장
데이터가 사용자의 휴대폰에서 서버까지 가려면 효율적이면서도 데이터를 잃지 않도록 3중 보장 메커니즘이 필요합니다:
첫 번째: 모아서 보내기(배치 집계)
SDK(트래킹 도구 패키지)는 기록이 생성될 때마다 전송하지 않고, 먼저 휴대폰 메모리에 임시 보관합니다. 일정 수량(예: 30건)이 모이거나, 일정 시간(예: 5초)이 지나면 이 배치를 패키징하여 한 번에 전송합니다.
택배에 비유하면: 물건을 하나 살 때마다 택배를 보내는 것이 아니라 몇 개를 모아 함께 보내면 시간과 노력이 절약됩니다. 휴대폰의 경우 이렇게 하면 네트워크 요청 횟수를 줄여 배터리와 데이터를 절약할 수 있습니다.
두 번째: 오프라인에서도 안전(로컬 저장)
엘리베이터나 지하철 터널에서 휴대폰은 자주 네트워크 신호를 잃습니다. 데이터가 메모리에만 있으면 사용자가 앱을 닫을 때 데이터가 사라집니다.
그래서 SDK는 아직 전송하지 못한 데이터를 휴대폰의 로컬 저장소에 보관합니다(편지를 서랍에 먼저 넣어두는 것과 유사). 네트워크가 복구되면 자동으로 이 데이터를 재전송합니다. 이렇게 하면 사용자가 일시적으로 오프라인이 되어도 데이터가 손실되지 않습니다.
세 번째: 서버 과부하 방지(메시지 큐)
데이터가 서버에 도착한 후 바로 데이터베이스에 기록되는 것은 아닙니다. 왜요? 프로모션 등 피크 시간에는 초당 수만 건의 데이터가 한꺼번에 몰려올 수 있어, 데이터베이스가 이를 직접 처리하면 다운될 수 있기 때문입니다.
해결책은 중간에 "버퍼 영역"을 추가하는 것입니다. 기술적으로 메시지 큐(대표적 도구로 Kafka)라고 부릅니다. 식당의 번호표 대기 시스템과 같은 역할을 합니다: 피크 시간에는 고객(데이터)이 먼저 줄을 서서 기다리고, 주방(데이터베이스)이 자신의 속도에 맞춰 하나씩 처리하며, 한꺼번에 몰려드는 주문에 압도당하지 않습니다.
이 단계 요약: "모아서 보내기 → 오프라인 로컬 저장 → 메시지 큐 버퍼링"의 3중 보장을 통해 데이터가 안전하게 서버에 도착했습니다.
하지만 한 가지 문제가 더 있습니다: 네트워크가 끊겼다가 재연결되면 데이터가 자동으로 재전송되므로, 같은 기록이 두 번 전송될 수 있습니다. 이를 처리하지 않고 그대로 데이터베이스에 저장하면 데이터가 중복됩니다(예: 10만 원짜리 주문이 두 건으로 기록되면 매출이 부풀려집니다). 그래서 다음 단계에서는 데이터를 "정제"해야 합니다.
4단계: 정제와 적재 — 데이터를 정리하고 "더러운 데이터" 제거
전제 조건: 데이터가 전송 파이프라인을 통해 안전하게 서버에 도착했습니다.
이 단계 목표: 데이터가 공식적으로 데이터베이스에 저장되기 전에 "건강 검진"을 실시합니다 — 중복을 제거하고, 형식에 문제가 있는 것을 수정하여, 최종적으로 저장되는 데이터가 깨끗하고 정확하도록 합니다.
왜 정제가 필요한가? 택배 한 상자를 받은 후 확인하는 것과 같습니다: 중복 배송은 없는지? 잘못 배송된 것은 없는지? 포장이 훼손된 것은 없는지? 데이터도 마찬가지로 데이터베이스에 저장하기 전에 먼저 확인하고 정리해야 합니다.
이 과정은 기술적으로 ETL이라고 부르며, 세 영어 단어의 약자입니다:
- Extract(추출): 메시지 큐에서 데이터를 꺼냄
- Transform(변환): 데이터 형식을 확인하고 수정
- Load(적재): 정제된 데이터를 데이터베이스에 기록
id-001 userId: "zhang" add_to_cart ¥2999id-001 userId: "zhang" add_to_cart ¥2999重复id-002 user_id: "li" click_buy ¥0id-003 userId: "wang" pay 1970-01-01时间异常id-004 user_id: "zhao" click_buy ¥599id-001 user_id: "zhang" add_to_cart ¥2999id-002 user_id: "li" click_buy ¥0id-004 user_id: "zhao" click_buy ¥599💡 핵심 원리: 데이터 정제의 두 가지 핵심 동작
동작 1: 중복 제거 — 중복 기록 제거
앞서 언급했듯 네트워크 재연결 후 SDK가 데이터를 자동 재전송하여 같은 기록이 여러 번 전송될 수 있습니다. 어떻게 중복을 식별할까요?
방법은 간단합니다: 클라이언트에서 데이터를 패키징할 때 각 기록에 전 세계적으로 유일한 번호를 할당합니다(dedup_id라고 하며, 택배 송장 번호와 유사). 서버는 데이터를 저장하기 전에 이 번호가 이미 존재하는지 확인합니다 — 이미 존재한다면 중복 데이터이므로 바로 폐기합니다.
동작 2: 검증과 형식 통일 — 비표준 기록 수정
애플리케이션은 계속 버전이 업데이트되며, 버전마다 트래킹 코드에 미세한 차이가 있을 수 있습니다. 예를 들어:
- 구버전은 사용자 ID 필드를
userId로 명명했지만, 신버전은user_id로 변경 - 일부 기록의 타임스탬프가 분명히 비합리적(예: 1970년으로 표시)
- 일부 필드의 값을 인식할 수 없음
이 단계에서 시스템은 변환 규칙을 작성하여 이러한 문제를 통일적으로 처리합니다. 필드명이 일치하지 않는 것은 통일하고, 타임스탬프가 비정상인 기록은 폐기하며, 인식할 수 없는 값은 unknown으로 표시합니다.
이 단계 요약: 중복 제거와 형식 검증 후, 데이터는 깨끗하고 통일된 형태로 데이터 웨어하우스(대량의 데이터를 저장하고 분석하는 데 특화된 데이터베이스로, ClickHouse, Hive 등이 대표적)에 기록됩니다. 데이터 분석가는 SQL 문으로 이 데이터를 직접 조회하여 신뢰할 수 있는 분석 결과를 얻을 수 있습니다.
전체 프로세스 되돌아보기
다음은 데이터 트래킹의 수집부터 적재까지 4단계 프로세스 요약입니다:
| 단계 | 수행 내용 | 획득 결과 | 남은 문제 |
|---|---|---|---|
| 1. 수집 방안 선택 | 어떤 방식으로 사용자 조작을 기록할지 결정 | 애플리케이션에 기록 능력 부여 | 각 기록 장치의 데이터 형식이 비표준적 |
| 2. 데이터 형식 설계 | 4W1H 템플릿으로 기록 형식 통일 | 모든 기록이 표준 JSON | 사용자가 많을 때 건별 전송으로 감당 불가 |
| 3. 전송과 캐싱 | 배치 전송, 오프라인 저장, 큐 버퍼링 | 데이터가 안전하게 서버에 도착 | 재시도로 인한 데이터 중복 가능 |
| 4. 정제와 적재 | 중복 제거, 검증, 형식 통일 | ✅ 깨끗한 데이터가 데이터 웨어하우스에 저장 | — |
맺음말
사용자가 애플리케이션에서 버튼을 클릭할 때, 겉으로는 순간적인 동작일 뿐입니다. 하지만 그 이면에서는 완전한 데이터 파이프라인이 이미 가동되고 있습니다:
- 트래킹 코드가 이 클릭을 포착하고 4W1H 템플릿에 따라 표준 기록을 생성합니다
- 기록이 휴대폰에 임시 보관되었다가 충분히 모이면 서버로 일괄 전송됩니다
- 서버는 메시지 큐를 통해 안정적으로 수신하고, 중복 제거와 형식 검증을 거칩니다
- 마침내 깨끗하고 정확한 데이터가 데이터 웨어하우스에 기록됩니다
이것이 데이터 트래킹의 전체 과정입니다. 사용자의 흩어져 있고 보이지 않는 조작 행위를 조회하고 분석할 수 있는 구조화된 데이터로 변환합니다. 제품 매니저는 어떤 기능을 사용자가 좋아하는지, 어디서 이탈하는지 파악할 수 있고, 운영 담당자는 이벤트 효과를 평가할 수 있으며, 개발자는 문제가 어느 버전에서 발생했는지 파악할 수 있습니다.
이 "수집 → 모델링 → 전송 → 정제" 체계는 데이터 기반 의사결정의 기반 인프라입니다.