Google Tag Gateway: 방문자 분석에서 애플의 벽을 넘는 법, 정답은 Cloudflare에 있었어

Cover Image for Google Tag Gateway: 방문자 분석에서 애플의 벽을 넘는 법, 정답은 Cloudflare에 있었어

swhan

· 11 min read

애플의 강력한 프라이버시 정책으로 GA4 데이터가 누락될 때, Cloudflare 기반 Google Tag Gateway로 태그 로드·이벤트 전송을 퍼스트파티 도메인에서 처리해서 신호 손실을 최소화하는 방법을 공유합니다.

요약

  • 애플 Safari 브라우저는 서드파티 쿠키와 크로스 사이트 트래킹을 강하게 제한함.
  • 방문자 분석에 사용되는 스크립트 로딩, 분석 데이터 전송에 사용하는 도메인을 자체 소유 도메인으로 바꿔버리면 Safari 유저들의 데이터도 정상적으로 분석 가능함
  • 이를 위해 Google Cloud Platfrom의 Cloud Run에서 사용할 수 있는 서버사이드 GTM(Google Tag Manager)이라는 방법이 있지만 비싸고 설정이 복잡함
  • Cloudflare에서 제공하는 프록시 서비스인 Google Tag Gateway를 이용하면 공짜로 복잡한 과정들(운영, 모니터링, 로깅)을 신경 쓸 필요 없이 방문자 분석이 가능함

들어가며

Google Tag Gateway를 한 줄로 설명하면 방문자 분석 정보를 사용자 소유의 1st-party 도메인을 통해 전송하기 위해서 사용하는 프록시 레이어입니다.

Google Tag Gateway Overview

왜 사용하는지 알아보기 전에 우선 Google 태그가 무엇인지 알아야 합니다.

그동안 저희는 블로그 방문자 분석에 사용할 Google 태그를 웹페이지에 직접 추가하기 위해 gtag.js라는 자바스크립트 라이브러리를 사용하고 있었는데요, Google 태그란 Google 애널리틱스 4(GA4)에 데이터를 전송하기 위한 도구라고 보시면 됩니다.

gtag.js 라이브러리를 호출하기 위해서는 googletagmanager.com 도메인을 이용해야 하는데요, Apple의 Safari는 사용자 추적을 제한하기 위해 Intelligent Tracking Prevention(ITP)을 강화해 옴으로써 Google 태그/GA4 신호가 축소되며 개인정보 보호 브라우징이나 고급 추적 보호 사용 시 스크립트 요청이 차단되기도 합니다. (구글이 만든 브라우저답게 애플 기기라도 Chrome 브라우저에서는 정상 작동합니다)

safari known tracker 차단 스크린샷

gtag.js는 내부적으로 퍼스트파티 쿠키(.day1swhan.com)를 사용하니 저희는 스크립트 로딩, 분석 데이터 전송을 구글 소유의 도메인이 아닌, 저희 소유의 도메인으로 바꿔버리면 Safari 사용자의 데이터도 정상적으로 수집할 수 있습니다.

gtag.js vs GTM

용어 정리

마케터가 아닌 개발자의 입장에서 Google Tag Gateway를 왜 사용해야 하는지 알기 위해서는 Google 태그, gtag.js, GTM의 개념과 차이를 먼저 이해해야 합니다.

우선 구글이 공식적으로 설명하는 버전으로는 다음과 같습니다.

  • Google 태그: Google Tag. 단일 통합 태그 시스템
  • gtag.js: Global Site Tag. Google 태그를 삽입하는 자바스크립트 라이브러리
  • GTM: Google Tag Manager. Google 태그를 포함해 다양한 태그(GA4, Facebook Pixel, Google Ads)를 관리할 수 있는 컨테이너

확실히 전 세계 천재들이 모인 스타트업인 구글답게 저같은 일반인은 이해가 잘 가지 않는데요, 우선 가장 중요한 태그(Tag)란 웹페이지 또는 모바일 앱에서 실행되는 코드 스니펫이라고 이해하시면 됩니다.

이제 아이큐 두 자리도 이해할 수 있게 설명하면 다음과 같습니다.

  • Google 태그: 단일 태그(코드 스니펫)로 각종 Google 제품 및 서비스(Ads, Analytics, Campaign Manager)에 보내는 데이터 추적 시스템. 데이터 수집&전송 기능을 담당
  • gtag.js: Google 태그를 코드를 이용해서 직접 사이트에 추가할 때 사용하는 자바스크립트 라이브러리
  • GTM: Google 태그를 UI에서 관리하는 태그 관리 시스템(다양한 태그들을 한 곳에서 관리 가능)

참고: 전체 사이트 태그(gtag.js)의 이름이 Google 태그로 변경되고, 여러 플랫폼에서 작동하는 태그들의 중앙 집중식 관리를 위해 구글 태그 매니저(Google Tag Manager)가 생겨났는데요, 구글의 복잡한 네이밍 센스는 언제나 따라가기 힘든 것 같습니다.

용어 정의가 더 궁금하신 분들은 구글 공식 문서인 태그 관리자 및 Google 태그(gtag.js)를 참고해주세요.

gtag.js(Global Site Tag)

아직도 추상적일 수 있으니 개발자답게 gtag.js의 실제 작동 방식을 보면서 알아보겠습니다.

<!-- gtag.js를 이용한 Google 태그 초기화 방법 -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag() {
    // 함수 인자로 들어온 값들을 알잘딱으로 배열로 넘겨줌.
    // 개발자들은 순서(이벤트 타입, 이벤트 이름, 추가정보)만 신경쓰면 됨
    dataLayer.push(arguments);
  }
  gtag("js", new Date());
  gtag("config", "G-XXXXX");
</script>
gtag("event", "page_view");

개발자들에게 익숙한 이 코드는 gtag.js를 이용해 Google 태그를 초기화하고, 특정 게시글 페이지 방문 시 저희가 작성한 gtag 함수를 이용해 page_view 이벤트를 Google 태그에 직접 삽입하는 방식입니다.

Google 태그는 config 명령을 통해 사전에 설정된 구글 제품들(GA4, Ads)을 식별하고, 이벤트 이름과 매개변수 객체를 바탕으로 연결된 대상(GA4, Ads)에게 전달하기 위해서 내부적으로 아래 작업들을 처리하는데요

  • 구성 통합: 여러 태그(GA4, Ads)가 하나의 Google Tag 인스턴스를 공유
  • 기본 Context 관리: 측정 ID, 세션, 사용자, 쿠키 등 기본 context를 생성
  • 이벤트 라우팅: 어떤 GA4 property로 이벤트를 보낼지 결정
  • 데이터 표준화: page_view, click, purchase 등 이벤트를 GA4 용으로 포맷팅
  • 네트워크 전송: Measurement Protocol 형식으로 실제 요청 생성 및 전송

도식으로 표현하면 다음과 같습니다.

gtag('event', 'page_view')
   ↓
(window.dataLayer)
   ↓
[Google 태그]
  - 공통 context 관리
  - 이벤트 표준화
  - Measurement Protocol payload 생성
   ↓
[Measurement Protocol v2 요청]
   ↓
GA4 수집 서버(google-analytics.com)

gtag.js 요청 흐름

이제 브라우저 개발자 도구를 이용해서 GA4에 전송되는 Measurement Protocol 요청을 보시면 저희 목적인 블로그 방문자 분석에서 가장 중요한 페이지 뷰 이벤트를 보실 수 있습니다. (전송되는 정보가 이것저것 많지만, 저희는 블로그 방문자들의 페이지 뷰, 유입 경로, 기기 유형 정보만 필요하니 굳이 모든 것을 알 필요는 없습니다)

POST /g/collect?key1=value1&key2=value2...

v: 2
tid: G-XXXX
gtm: XXXX...
...
sid: 123456
en: page_view # 이벤트 이름
dl: https://blog.day1swhan.com/articles/build-blog-comments-api-with-workers-kv # 페이지 링크
dt: Next.js SSG 블로그에 댓글 API 연동하기 | day1swhan 블로그 # 페이지 제목
dr: https://blog.day1swhan.com/ # Referral

GTM(Google Tag Manager)

GTM에서 사용하는 용어 먼저 살펴보겠습니다.

  • 트리거: 이벤트 실행 조건 (All Pages, Scroll Depth, Click)
  • 태그: 조건 만족 시 실행할 코드 스니펫(GA4, Ads)
  • 이벤트: 이벤트 정보(page_view, scroll, purchase, share)
<!-- GTM을 이용한 Google 태그 초기화 방법 -->
<script>
  (function (w, d, s, l, i) {
    w[l] = w[l] || [];
    w[l].push({ "gtm.start": new Date().getTime(), event: "gtm.js" });
    var f = d.getElementsByTagName(s)[0],
      j = d.createElement(s),
      dl = l != "dataLayer" ? "&l=" + l : "";
    j.async = true;
    j.src = "https://www.googletagmanager.com/gtm.js?id=" + i + dl;
    f.parentNode.insertBefore(j, f);
  })(window, document, "script", "dataLayer", "GTM-XXXX");
</script>

이렇게 GTM 컨테이너를 로드 후 Google 태그를 초기화하는데요, gtag.js 방식과 차이점은 코드를 이용해서 Google 태그에 직접 이벤트를 발행하는 방식이 아닌, 웹 UI에서 트리거만 등록해놓으면 GTM이 내부적으로 gtag.js를 이용해서 Google 태그에 이벤트를 발행하는 방식입니다.

순서대로 확인해 보면

  1. 사용자가 페이지에서 스크롤 / 클릭 / 로드 / 구매 등의 행동 발생
  2. GTM의 트리거가 해당 조건을 감지
  3. 연결된 태그가 실행됨(여기서는 GA4)
  4. GTM 내부에서 gtag.js 코드가 호출됨 (자동)
  5. GA4 수집 서버로 Measurement Protocol v2 요청이 전송됨(google-analytics.com)

GTM 방식이 뭔가 더 편해 보이긴 하지만 저희는

  1. 여러 태그들(Ads, Facebook Pixel)을 관리하지도 않음
  2. 수집 대상(웹, iOS, Android)도 여러 개가 아닌 웹 클라이언트 1개
  3. gtag.js향상된 측정 옵션 활성화만으로 웬만한 이벤트는 자동으로 처리함
  4. 코드를 이용하는 게 이벤트를 더 정교하게 다룰 수 있음

이런 상황이니 굳이 GTM이 아닌, 기존 사용하던 gtag.js 방식을 유지하도록 하겠습니다.

gtm client side tagging diagram

Server-side GTM(Google Tag Manager)

gtag.js, GTM의 핵심은 이벤트를 직접 코드로 생성하는지, 웹 UI에서 트리거를 등록하고 자동으로 처리하는지라고 볼 수 있는데요, 하지만 이벤트의 최종 목적지는 결국 Safari가 브라우저 레벨에서 차단하는 google-analytics.com인 것을 알 수 있습니다.

# gtag.js, GTM 요청 흐름
[Browser]
  ↓ (loads JS)
[googletagmanager.com]
  ↓ (sends data)
[google-analytics.com]

이걸 저희 소유 도메인에서 작동할 수 있도록 GA 서버 앞에 위치시켜서(리버스 프록시) 브라우저 입장에서는 1st-party 리소스로 인식시키기는 방식이 Server-side GTM입니다.

# Server-side GTM
[Browser]
  ↓ (loads JS)
[googletagmanager.com]
  ↓ (sends data)
[analytics.example.com] ← my server
  ↓ (repackages + forwards)
[google-analytics.com]

gtm server side tagging diagram

서버사이드 GTM을 이용하기 위해서 자체 소유 서버나 Google Cloud Platform(GCP)의 Cloud Run에서 작동하는 컨테이너(태그 관리 서버)가 필요한데요, 이를 위해서 구글은 Docker 이미지를 제공하고 있습니다.

태그 관리 서버(컨테이너)가 하는 일을 크게 보면 사용자의 기기에서 웹 요청을 수신 후 이벤트로 변환하고, 각 이벤트는 저희가 설정한 태그, 트리거, 변수에 의해 처리된 다음, GA 서버로 전송해 주는 것입니다.

저희는 다루는 이벤트가 많지도 않고, 이벤트를 따로 가공할 필요도 없는데 굳이 Cloud Run 인스턴스까지 사용할 필요는 없으니 Server-side GTM 방식도 탈락입니다.(vCPU 1개, 메모리 0.5GB 인스턴스 $45/월)

Google Tag Gateway

Google Tag Gateway with Cloudflare

Cloudflare는 이렇게 복잡한 태그 관리 기능이 필요하지 않고, 방문자 분석 기능을 브라우저에게 1st-party 리소스로만 인식시키면 충분한 사람들을 위해서 태그 로드(googletagmanager.com)와 이벤트 전송(google-analytics.com)은 모두 퍼스트파티 도메인으로 처리한 뒤, 이를 Google로 중계만 해주는 프록시 서비스인 Google Tag Gateway통합을 제공합니다. (광고주를 위한 거라고 홍보하는데 개인적으로 개발자를 위한 서비스라고 홍보해도 좋을 것 같습니다)

설정 방법도 5분이면 끝날 정도로 단순한데요

cloudflare google tag gateway 설정 옵션 스크린샷

저희가 관리하는 Google 태그 관리자 홈페이지 - 관리자 탭에서 Google 태그 게이트웨이 옵션 활성화, Cloudflare 자동 설정을 위해 로그인 후 Google 태그 ID와 측정 데이터가 전송될 경로를 설정해 주시면

cloudflare google tag gateway 설정 완료 스크린샷

자체 도메인에서 스크립트 로드 후 방문자 분석 정보를 Google 태그 → 태그 게이트웨이 → GA4 순서로 전송할 수 있습니다. 이제 Google 태그 초기화 시 server_container_url프록시 적용된 Cloudflare에서 도메인 + 측정 데이터가 전송될 경로로 등록해 줍니다.

저는 blog 서브도메인은 Vercel에서 호스팅 중이어서 Cloudflare 프록시가 작동하지 않으니, Workers를 이용한 서버리스 블로그 방문자 카운팅 API 만들기 2편에서 사용한 analytics 서브도메인과 metrics 경로를 사용하겠습니다.

window.dataLayer = window.dataLayer || [];
function gtag() {
  dataLayer.push(arguments);
}
gtag("js", new Date());
gtag("config", "G-XXXX", {
  server_container_url: "https://analytics.day1swhan.com/metrics", // 경로 주의
  ...
});

주의사항

Google Tag Gateway에서 사용할 도메인은 Cloudflare에서 프록시 적용 중이어야 하고(요청 가로챈 후 구글 쪽으로 라우팅 처리해야 하니까), 측정 데이터 전송 경로(/metrics)는 사용할 도메인에서 서비스 중인 기능에 영향을 미치지 않도록 사용하지 않는 경로를 선택하셔야 합니다.

Google Tag Gateway 도메인 분리

DNS 설정만으로 Gateway에서 사용할 도메인을 아예 분리하고 싶다면 DNS 질의에는 응답하지만 실제 트래픽은 Cloudflare가 프록시로 가로채 처리할 수 있도록 문서화/실험용 IP인 192.0.2.1A 레코드로 등록해 주시면 됩니다.

TEST-NET-1 DNS 프록시 설정 스크린샷

curl 명령어 이용해서 도메인 호출해 보면 프록시 작동 중이고, gtag.js 스크립트도 정상적으로 가져오는 것을 보실 수 있습니다.

# 문서화/실험용 IP > 응답 확인 > CF 프록시가 알아서 처리함
nslookup tag-gateway.day1swhan.com

Non-authoritative answer:
Name:	tag-gateway.day1swhan.com
Address: 104.21.87.14
Name:	tag-gateway.day1swhan.com
Address: 172.67.139.37
# 문서화/실험용 IP > CF 프록시 > gtag.js 호출
curl -I 'https://tag-gateway.day1swhan.com/metrics/gtag.js'

HTTP/2 200
...
content-type: application/javascript; charset=UTF-8
server: cloudflare

사파리 개인정보보호 브라우징에서도 1st-party 도메인으로 처리되면서 Measurement Protocol이 Gateway를 통해서 Google Analytics 서버로 잘 전송되는 것을 보실 수 있습니다.

safari private mode Google Tag Gateway 정상 작동 스크린샷

마무리

까칠한 애플의 프라이버시 정책은 계속 변하겠지만, 데이터를 잃지 않으려는 구글과 개발자들도 멈추지 않고 계속 발전하는 게 재밌는 것 같습니다. 구글과 Cloudflare가 앞으로도 쭉 좋은 서비스들 출시해 주길 바라며 포스팅 마무리하겠습니다.


더보기


댓글

0/500