GA4 + GTM 마스터 노트 시리즈 3편. 자동 수집 4가지, 향상된 측정 6가지, 커스텀 이벤트 구현까지 — gtag.js와 dataLayer.push 두 채널, HTML 데이터 속성과 JavaScript 전역 변수로 데이터 추출, 사용자 속성 vs 이벤트 매개변수, User ID 크로스-디바이스 추적까지 코드와 함께 풀어 갑니다.
이 글은 GA4 + GTM 마스터 노트 시리즈의 세 번째 편입니다. 1편(입문)에서 GA4의 이벤트 모델을, 2편(GTM)에서 도구를 잡았다면, 이번엔 본격 이벤트 구현 입니다.
GA4의 모든 데이터는 이벤트를 통해 들어와요. 페이지뷰도 이벤트, 클릭도 이벤트, 구매도 이벤트. 이번 편은 그 이벤트를 자동 수집·향상된 측정·커스텀 이벤트 세 갈래로 나눠 어디서 어떻게 데이터를 얻는지 풀어 갑니다.
처음 이벤트 구현이 어렵게 느껴지는 이유
이유는 두 가지예요.
첫째, 데이터를 어디서 가져올지가 첫 단계에서 막힙니다. 클릭한 상품의 ID·이름·가격을 어떻게 알아내지? HTML data 속성? JavaScript 전역 변수? localStorage? REST API? 선택지가 많아 처음엔 혼란.
둘째, 이벤트 매개변수와 사용자 속성의 차이가 헷갈려요. 둘 다 이벤트에 정보를 첨부하는 메커니즘인데, 한쪽은 이벤트마다 새로 보내고 한쪽은 한 번 설정하면 sticky하게 따라다닙니다. 어떤 자리에 어떤 걸 써야 하는지 첫 단계에서 안 보여요.
해결법은 두 가지. 첫째, 데이터 추출 패턴 4가지(data-* 속성·전역 변수·DOM 추출·localStorage)를 한 번 외우면 어떤 추적도 같은 패턴 반복이에요. 둘째, "이 정보는 사용자에게 따라다녀야 하는가?" 한 질문으로 매개변수와 속성을 구분합니다. 사용자 속성은 sticky, 매개변수는 일회성.
이벤트 전송 — 두 가지 채널
GA4로 이벤트를 보내는 방법은 두 가지예요.
방법 1 — gtag.js 직접 호출
// 가장 단순한 형태
gtag('event', 'event_name', {
'parameter_key': 'parameter_value'
});
// 예시 — 상품 클릭
gtag('event', 'select_content', {
'content_type': 'product',
'item_id': 'SKU-001'
});
장점 — 즉시 GA4로 전송. 단순. 단점 — 코드를 사이트에 직접 박아야 해서 GTM의 유연성 손실.
방법 2 — dataLayer.push (GTM 방식)
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'event': 'product_click',
'product_id': 'SKU-001',
'product_name': 'Blue T-Shirt',
'product_category': 'Clothing'
});
장점 — GTM Custom Event 트리거가 감지해서 GA4 Event 태그로 발동. 마케터가 GTM에서 자유롭게 이벤트 매핑 가능. 단점 — GTM 설정 단계가 추가됨.
여기서 정말 중요한 시험 함정 — dataLayer.push는 GA4로 직접 전송이 아닙니다. dataLayer에 푸시한 뒤 GTM에서 Custom Event 트리거 + GA4 Event 태그를 만들어야 GA4로 흘러가요. 이 두 단계를 빼먹으면 dataLayer는 잔뜩 쌓이는데 GA4 보고서가 비어 보입니다.
이벤트 명명 규칙
GA4 이벤트 이름은 다음 룰을 따라야 해요.
- 소문자 사용
- 공백 대신 언더스코어(_)
- 영문자로 시작
- 최대 40자
- 예약된 이름 사용 불가 (
app,audience등 GA4가 쓰는 이름)
✅ 좋은 예: product_click, add_to_cart, video_play_25
❌ 나쁜 예: ProductClick (대소문자 혼용)
1stClick (숫자로 시작)
purchase event (공백)
자동 수집 이벤트 4가지
코드 한 줄 안 써도 GA4 측정 코드만 박혀 있으면 자동으로 발생하는 이벤트들.
| 이벤트 | 발동 시점 | 주요 매개변수 |
|---|---|---|
first_visit |
새 기기/브라우저 첫 방문 | - |
session_start |
새 세션 시작 | session_id |
page_view |
페이지 로드 시 | page_location, page_title |
user_engagement |
페이지 포커스 중 | engagement_time_msec |
page_view 이벤트가 자동으로 보내는 데이터.
{
'event': 'page_view',
'page_location': 'https://example.com/products',
'page_referrer': 'https://google.com',
'page_title': '상품 목록 - My Store'
}
향상된 측정 — 6가지 자동 추적
Admin → Data Streams → 스트림 선택 → Enhanced measurement 토글 활성화 시 추가로 자동 추적되는 이벤트.
Scroll — 90% 도달
{ 'event': 'scroll', 'percent_scrolled': 90 }
Outbound Click — 외부 링크
{
'event': 'click',
'link_domain': 'external-site.com',
'link_url': 'https://external-site.com/page',
'outbound': true
}
Site Search — 검색 쿼리 자동 감지
URL에 검색 쿼리 파라미터가 있으면 자동 발동.
// example.com/search?q=shoes
{ 'event': 'view_search_results', 'search_term': 'shoes' }
쿼리 파라미터 이름은 GA4에서 커스텀 가능 (기본값 q).
Video Engagement — YouTube 임베드
// 시작
{ 'event': 'video_start', 'video_title': '...', 'video_percent': 0 }
// 25%, 50%, 75% 진행
{ 'event': 'video_progress', 'video_percent': 25 }
// 완료
{ 'event': 'video_complete', 'video_percent': 100 }
File Download — 특정 확장자 자동 추적
pdf·xlsx·docx·csv·pptx 등 파일 링크 클릭 시 자동 발동.
{
'event': 'file_download',
'file_name': 'product-catalog.pdf',
'file_extension': 'pdf'
}
여기서 시험 함정이 하나 있어요. 이 6가지를 GTM 트리거로 따로 만드는 건 시간 낭비입니다. Admin 토글 한 번이면 자동 추적되는 걸 굳이 GTM에서 다시 만드는 학습자가 의외로 많아요. 향상된 측정 활성화부터 하고, 그게 부족할 때만 커스텀 이벤트로 추가합니다.
커스텀 이벤트 — 본격 추적
자동·향상된 측정으로 안 잡히는 행동은 직접 정의해야 해요. 가장 흔한 패턴 4가지.
패턴 (1) 상품 클릭 — 이벤트 위임
document.addEventListener('click', function(e) {
var target = e.target;
// 부모 요소 탐색 (이벤트 버블링)
while (target && !target.classList.contains('product-image')) {
target = target.parentElement;
}
if (target) {
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'event': 'product_click',
'product_id': target.getAttribute('data-product-id'),
'product_name': target.getAttribute('data-product-name'),
'product_category': target.getAttribute('data-category')
});
}
});
GTM 설정:
Tag: GA4 Event
Event Name: product_click
Parameters: product_id: {{DLV - product_id}}
Trigger: Custom Event
Event Name: product_click
패턴 (2) 메뉴 클릭 — GTM Click 트리거
Trigger: Click - All Elements
Condition: Click Classes contains page-item
Tag: GA4 Event
Event Name: menu_click
Parameters:
menu_name: {{Click Text}}
menu_url: {{Click URL}}
코드 한 줄 안 쓰고 GTM 설정만으로 추적 가능.
패턴 (3) 폼 제출 — 두 가지 방법
방법 A — GTM 폼 트리거:
Trigger: Form Submission
Wait for Tags: 체크
Check Validation: 체크
Form ID equals: contact-form
Tag: GA4 Event
Event Name: form_submit
Parameters:
form_id: {{Form ID}}
방법 B — JavaScript:
var form = document.getElementById('contact-form');
if (form) {
form.addEventListener('submit', function(e) {
window.dataLayer.push({
'event': 'form_submit',
'form_name': 'contact_form',
'has_email': form.querySelector('[name="email"]').value ? 'yes' : 'no'
});
});
}
패턴 (4) 별점 클릭
document.querySelectorAll('.star-rating').forEach(function(star) {
star.addEventListener('click', function() {
var rating = this.getAttribute('data-rating');
window.dataLayer.push({
'event': 'rating_click',
'rating_value': rating,
'product_id': document.querySelector('.product-id').value
});
});
});
데이터 추출 4가지 패턴
이벤트 매개변수에 채울 값을 어디서 가져올지의 4가지 패턴.
패턴 (1) HTML 데이터 속성 — 가장 권장
개발자가 HTML 요소에 추적용 데이터를 직접 박는 방법.
<div class="product-card"
data-product-id="SKU-001"
data-product-name="Blue T-Shirt"
data-product-category="Clothing"
data-product-price="29.99">
<img src="product.jpg" class="product-image">
<button class="add-to-cart">장바구니 담기</button>
</div>
GTM에서 DOM 요소 변수로 접근:
Variables → DOM Element
Selection Method: CSS Selector
Element Selector: .product-card
Attribute Name: data-product-id
또는 JavaScript에서:
function getProductData(clickedElement) {
var card = clickedElement.closest('.product-card');
if (!card) return null;
return {
id: card.getAttribute('data-product-id'),
name: card.getAttribute('data-product-name'),
category: card.getAttribute('data-product-category'),
price: parseFloat(card.getAttribute('data-product-price'))
};
}
여기서 정말 중요한 시험 함정 — 데이터 속성 패턴이 가장 안정적입니다. CSS 셀렉터로 DOM을 뒤지는 방식은 사이트가 리뉴얼될 때마다 깨져요. data 속성은 명시적이고 SEO·CSS와 무관해서 잘 안 깨집니다.
패턴 (2) JavaScript 전역 변수
서버에서 PHP 등으로 페이지에 전역 변수를 출력하는 방식.
// PHP에서 JavaScript 변수 출력
<script>
var productData = {
id: "<?php echo $product->id; ?>",
name: "<?php echo esc_js($product->name); ?>",
price: <?php echo $product->price; ?>
};
var pageData = {
type: "product",
user_id: "<?php echo $user_id; ?>",
is_logged_in: <?php echo $logged_in ? 'true' : 'false'; ?>
};
</script>
GTM Custom JavaScript 변수로 접근:
function() {
return window.productData ? window.productData.id : '';
}
패턴 (3) DOM에서 직접 추출
데이터 속성도 전역 변수도 없는 자리에서 마지막 수단.
function() {
var cartElement = document.getElementById('cart-total');
if (cartElement) {
return cartElement.innerText;
}
return '';
}
리스크 — 사이트 리뉴얼 시 즉시 깨짐. 클래스명·ID 한 글자 바뀌면 추적 실패.
패턴 (4) localStorage — 사용자 속성 영구 저장
브라우저를 닫아도 유지되는 데이터 저장소.
function setUserProperty(key, value) {
localStorage.setItem('ga_user_' + key, value);
}
function getUserProperty(key) {
return localStorage.getItem('ga_user_' + key);
}
// 사용
setUserProperty('affinity', 'electronics');
var affinity = getUserProperty('affinity'); // 'electronics'
이벤트 매개변수 모범 사례
GA4 표준 매개변수 — 이름을 맞추는 게 중요
| 매개변수 | 유형 | 예시 |
|---|---|---|
item_id |
문자열 | "SKU-001" |
item_name |
문자열 | "Blue T-Shirt" |
item_category |
문자열 | "Clothing" |
item_category2 |
문자열 | "T-Shirts" |
price |
숫자 | 29.99 |
quantity |
숫자 | 2 |
currency |
문자열 | "USD" |
coupon |
문자열 | "SAVE10" |
// 좋은 예 — GA4 표준 명칭
{
'event': 'product_click',
'item_id': 'SKU-001',
'item_name': 'Blue T-Shirt',
'item_category': 'Clothing',
'price': 29.99
}
// 나쁜 예 — 비표준 명칭
{
'event': 'ProductClick', // 대소문자 혼용
'prod_ID': 'SKU-001', // 비표준
'Price': 29.99 // 대문자 시작
}
여기서 시험 함정이 하나 있어요. GA4 표준 매개변수 이름을 정확히 따르면 보고서·머신러닝·이커머스 분석이 자동으로 활성화됩니다. item_id로 박으면 자동 인식되지만 productId로 박으면 GA4가 모릅니다. 이름 한 글자가 분석 가능 여부를 갈라요.
사용자 속성 — 사용자에게 따라다니는 정보
이벤트 매개변수가 일회성이라면, 사용자 속성은 한 번 설정하면 다음 이벤트들에 자동으로 따라붙어요.
gtag('set', 'user_properties', {
'customer_type': 'premium',
'add_to_cart_user': 'yes',
'product_affinity': 'electronics'
});
이후 발생하는 모든 이벤트에 이 세 속성이 자동으로 첨부.
GTM Custom HTML로 설정
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('set', 'user_properties', {
'add_to_cart_user': 'yes'
});
</script>
카테고리 어피니티 구현 예시
사용자가 자주 보는 카테고리를 추적해서 사용자 속성으로 저장.
function updateCategoryAffinity(category) {
var current = localStorage.getItem('last_category');
if (current !== category) {
localStorage.setItem('last_category', category);
window.dataLayer.push({
'event': 'update_user_property',
'user_affinity': category
});
}
}
User ID — 크로스-디바이스 추적
1편에서 잠깐 본 User ID, 이번엔 구현까지.
4단계 구현
1단계 — 서버에서 User ID 출력:
echo "<script>var currentUserId = '" . $user_id . "';</script>";
2단계 — Cookie에 저장:
function setCookie(name, value, days) {
var expires = '';
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
expires = '; expires=' + date.toUTCString();
}
document.cookie = name + '=' + (value || '') + expires + '; path=/';
}
if (typeof currentUserId !== 'undefined') {
setCookie('ga_user_id', currentUserId, 30);
}
3단계 — GTM Cookie 변수:
Variables → New → 1st Party Cookie
Cookie Name: ga_user_id
4단계 — Google Tag에 전달:
Tag Configuration → Google Tag
Fields to Set:
user_id: {{Cookie - ga_user_id}}
마지막으로 GA4 설정:
GA4 Admin → Reporting Identity → By User ID and device
여기서 정말 중요한 시험 함정 — User ID에는 PII(개인정보)를 절대 넣지 마세요. 이메일·전화번호 직접 박으면 GA4 약관 위반. 서버에서 해시(SHA-256) 처리한 ID만 전달해야 합니다.
데이터레이어 — GTM 작업의 절반
// 데이터레이어 초기화 (GTM 스니펫 전에 선언)
window.dataLayer = window.dataLayer || [];
// 이벤트 푸시
window.dataLayer.push({
'event': 'event_name',
'key1': 'value1'
});
GTM 내부 이벤트
GTM이 자체적으로 발생시키는 이벤트들.
{ 'event': 'gtm.js' } // 컨테이너 로드
{ 'event': 'gtm.dom' } // DOM 준비
{ 'event': 'gtm.load' } // 페이지 완전 로드
{
'event': 'gtm.click',
'gtm.element': [DOM element],
'gtm.elementClasses': 'product-image'
}
데이터레이어 vs localStorage
| 특성 | 데이터레이어 | localStorage |
|---|---|---|
| 지속성 | 페이지 로드 시 초기화 | 브라우저 재시작 후에도 유지 |
| GTM 연동 | 직접 변수 접근 | Custom JS 변수로 |
| 용도 | 실시간 이벤트 | 사용자 속성·상태 |
이벤트 구현 체크리스트
구현 전 계획
- 추적할 사용자 행동 목록 작성
- 각 이벤트 매개변수 정의
- HTML 요소에 필요한 data 속성 추가 계획
- 개발자와 데이터 구조 협의
구현 단계
- GTM 컨테이너 설치 확인
- 트리거 생성 (조건 설정)
- 변수 생성 (데이터 추출)
- 이벤트 태그 생성
- Preview 모드에서 테스트
- GA4 DebugView에서 매개변수 확인
- Submit & Publish
시험 직전 한 번 더 — 자주 헷갈리는 함정 모음
여기까지가 3편의 핵심입니다. 시험 직전 또는 실무에서 헷갈릴 때 다시 펼쳐 볼 수 있게 압축 노트로 마무리할게요.
- 이벤트 전송 = gtag.js 직접 또는 dataLayer.push + GTM
- dataLayer.push만으로는 GA4로 안 감 — GTM Custom Event 트리거 + GA4 Event 태그 필수
- 이벤트 이름 — 소문자, 언더스코어, 영문 시작, 최대 40자
- 자동 수집 4개 — first_visit, session_start, page_view, user_engagement
- 향상된 측정 6개 = scroll·outbound·search·video·download (Admin 토글)
- 향상된 측정을 GTM으로 다시 만드는 건 시간 낭비
- 커스텀 이벤트 패턴 — 이벤트 위임·GTM Click 트리거·폼·별점
- 데이터 추출 4가지 —
data-*속성·전역 변수·DOM 추출·localStorage data-*속성이 가장 안정적 — 리뉴얼에 강함- DOM 직접 추출 = 클래스명 한 글자 바뀌면 깨짐
- GA4 표준 매개변수 이름(item_id, price, quantity) 정확히 사용
- 비표준 이름은 보고서·머신러닝 자동 활성 안 됨
- 사용자 속성 = sticky, 다음 이벤트들에 자동 첨부
- gtag('set', 'user_properties', {...}) 한 번이면 끝
- localStorage = 사용자 속성 영구 저장의 표준
- User ID 4단계 — 서버 출력 → Cookie 저장 → GTM 변수 → Google Tag 필드
- User ID에 PII 금지 — SHA-256 해시 처리한 값만
- GA4 Admin → Reporting Identity → By User ID and device
- 데이터레이어 vs localStorage — 일회성 vs 영구
- GTM 내부 이벤트 — gtm.js·gtm.dom·gtm.load·gtm.click
- 이벤트 구현 후 Preview + DebugView 둘 다 확인 필수
- 매개변수 25개 한도 / 사용자 속성 25개 한도
시리즈 다른 편
같은 시리즈의 다른 글들도 같은 톤으로 묶어 정리되어 있어요. 3편 이벤트 구현 패턴이 4편 전환·5편 이커머스에서 그대로 반복됩니다.
- 1편 — GA4 입문 (이벤트·세션·사용자 모델)
- 2편 — GTM 설정 (태그·트리거·변수)
- 3편 — GA4 이벤트 (현재 글)
- 4편 — 전환 이벤트와 잠재고객
- 5편 — 이커머스 추적 (Enhanced Ecommerce)
- 6편 — 탐색 보고서·Looker Studio·BigQuery
- 7편 — 디버깅과 모범 사례
공식 문서: GA4 Recommended Events에서 표준 이벤트 이름과 매개변수의 전체 목록을 확인할 수 있어요.
다음 글(4편)에서는 일반 이벤트를 전환으로 표시하는 방법, 잠재고객(Audience) 생성, 어트리뷰션 모델 비교, 맞춤 측정기준 등록까지 풀어 갑니다.