GA4 이벤트 — 자동·향상·커스텀 구현

2026-05-03확률과 통계 마스터 노트

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편 이커머스에서 그대로 반복됩니다.

공식 문서: GA4 Recommended Events에서 표준 이벤트 이름과 매개변수의 전체 목록을 확인할 수 있어요.

다음 글(4편)에서는 일반 이벤트를 전환으로 표시하는 방법, 잠재고객(Audience) 생성, 어트리뷰션 모델 비교, 맞춤 측정기준 등록까지 풀어 갑니다.

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

답글 남기기

error: Content is protected !!