Q. UTM 컨벤션을 정했는데 왜 채널 리포트가 계속 쪼개지나?
UTM은 자유 텍스트라서 우리가 만든 표는 우리만 읽고, GA4는 자기 규칙으로 따로 읽기 때문이다. 구글이 공개한 기본 채널 그룹 규칙은 source를 819개짜리 사이트 목록에 대 보고 medium을 정해진 정규식에 대 보는 방식이라, 같은 링크를 두고 우리 표는 referral이라 적어 두고 GA4는 Organic Search로 센다. 그래서 컨벤션 문서는 절반이고, 나머지 절반은 어긴 값을 BigQuery로 주기적으로 잡아내는 일이다.
오늘 바로 쓸 핵심 3줄
- 우리가 만든 channel_group 열은 GA4가 안 본다, 도구는 자기 목록과 정규식만 본다
- 매체가 채워 넣는 동적 파라미터는 우리 컨벤션을 지키지 않는다
- 컨벤션은 문서로 지켜지지 않고 주기적으로 도는 검증 쿼리로 지켜진다
여섯 달 뒤, 리포트에서 채널이 갈라져 있다
UTM은 처음 붙일 때가 제일 쉽지. 링크 뒤에 꼬리표 몇 개 달면 끝이고, 기초 편에서 다룬 그 다섯 칸이 전부니까.
문제는 반 년 뒤에 온다. 채널 리포트를 열었는데 같은 채널이 여러 줄로 갈라져 있고, 어느 줄이 진짜인지 아무도 모른다. 대소문자가 다른 값, 도메인을 붙인 값, 담당자가 바뀌면서 새로 생긴 값이 나란히 떠 있지.
그래서 컨벤션 표를 만든다. 매체별로 source와 medium을 정해 두고, 캠페인 이름 규칙을 박고, 보고서용 대분류까지 열로 달아 둔다. 여기까지는 대부분 하지. 그런데 표를 만들고 6개월 뒤에 다시 열어 보면 리포트는 여전히 쪼개져 있고.
이 글이 답하려는 건 세 가지다. 왜 표가 있는데도 쪼개지나. 그 표를 어떻게 적어야 도구와 싸우지 않나. 어긴 값을 사람이 아니라 쿼리로 어떻게 잡나.
같은 한 줄을 두 곳이 서로 다르게 읽는다
쪼개짐의 뿌리 자체는 단순한 편. UTM 값에는 의미가 없다. 그냥 문자열이고, 그걸 읽는 쪽이 각자 의미를 붙인다.
읽는 쪽은 최소 둘이지. 하나는 GA4의 기본 채널 그룹이고, 다른 하나는 우리가 만든 매핑표다. 둘은 같은 문자열을 보면서 서로 다른 기준으로 분류하고.
우리 표에 열을 하나 더 만든다고 GA4가 그 열을 읽어 주지는 않는다.
이 갈림이 왜 위험하냐면, 두 쪽이 쓰는 채널 이름이 겹치기 때문이다. 우리 표에도 social과 referral과 paid_search가 있고 GA4에도 Organic Social과 Referral과 Paid Search가 있지. 이름은 같은데 소속이 다르지.
그러면 회의실에 리포트 두 장이 올라오고, 둘 다 "소셜"이라고 적혀 있는데 숫자가 다르다. 정의가 다르다는 걸 아무도 눈치채지 못한 채 어느 쪽이 틀렸는지 다투게 되고.
우리 표를 구글 규칙에 대 보면 이렇게 갈린다
구글은 기본 채널 그룹의 판정 규칙을 공개해 뒀다. 요약하면 재료가 두 종류.
- 사이트 목록: source 값이 구글이 관리하는 목록에 있는지 본다. 검색, 소셜, 동영상, 쇼핑 네 갈래로 분류돼 있고 다운로드도 된다. 세어 보니 819개 항목이었다.
- medium 정규식: 유료 계열은
^(.*cp.*|ppc|retargeting|paid.*)$, 디스플레이는 display·banner·cpm 같은 고정 목록, 리퍼럴은 referral·app·link 셋뿐이다.
여기서 결정적인 대목 하나. Organic Search의 조건은 "source가 검색 사이트 목록에 있거나 medium이 정확히 organic"이다. 둘 중 하나만 맞으면 걸린다는 뜻이지. 그리고 목록에는 naver가 검색 사이트로 올라 있다.
그래서 utm_source를 naver로 통일해 둔 컨벤션은, medium이 무엇이든 유료 정규식에 안 걸리는 순간 전부 Organic Search로 떨어진다. 우리가 블로그라고 적든 카페라고 적든 상관없이.
| 우리가 붙인 라벨 | source / medium | 우리 channel_group | GA4 기본 채널 그룹 |
|---|---|---|---|
| 공식 블로그 | naver / blog | referral | Organic Search |
| 상위노출 대행 | naver / blog-top | referral | Organic Search |
| 브랜드검색 광고 | naver / bs | paid_search | Organic Search |
| 제휴 인스타 콘텐츠 | instagram / sns | paid_social | Organic Social |
| 메타 광고 (Facebook·Instagram 노출) | fb / da, ig / da | paid_social | Organic Social |
| 메타 광고 (그 밖 노출 위치) | an / da, msg / da | paid_social | Unassigned |
| 메신저 채널 발송 | kakao / message | social | Organic Social |
| 공식 영상 | youtube / video | video | Organic Video |
공개된 규칙에 값을 대입해 표로 옮긴 것이고, 실제 속성에서 확인한 값이 아니다.
세 줄만 짚어 보자. 브랜드검색 광고가 오가닉으로 잡힌다. medium을 bs로 쓰면 유료 정규식에 안 걸리고, source가 naver라 검색 사이트 조건에 걸려 버리니까. 광고비를 쓴 유입이 리포트에서는 공짜 유입으로 앉아 있게 되지.
제휴로 돈을 준 인스타 콘텐츠도 오가닉 소셜이 된다. Paid Social 조건은 소셜 사이트이면서 medium이 유료 정규식에 걸릴 것을 함께 요구하는데, sns는 그 정규식을 통과하지 못한다.
제일 고약한 건 마지막 줄이다. 메타 광고 한 줄이 노출 위치에 따라 Organic Social과 Unassigned로 갈린다. 왜 그런지는 조금 뒤 동적 파라미터 절에서 이어서 보자.
왜 "아마 이렇게 될 것이다"가 아니라 단정했나. 위 표의 각 줄은 조건을 만족하는 규칙이 정확히 하나뿐인 조합만 골랐다. 예를 들어 naver / cpc는 Paid Search와 Organic Search 두 조건을 동시에 만족해서 규칙 평가 순서를 알아야 결론이 나는데, 구글 문서가 순서를 명시하지 않아 표에서 뺐다. 우리 값이 이 부류라면 실제 속성에서 확인하는 편이 빠르다.
그래서 컨벤션은 두 층으로 나눠 적는다
여기까지 오면 컨벤션 표에 왜 channel_group 같은 열이 따로 필요한지가 분명해진다. UTM 다섯 칸에 담는 값과, 우리가 보고서에서 쓰는 분류는 아예 다른 층이거든.
파싱할 값과 매핑할 값을 가른다
둘을 가르는 기준은 하나뿐. 값이 유한하고 사람이 정하나, 아니면 계속 늘어나나.
medium은 유한하다. 우리가 정한 열몇 개가 전부이고 새 값이 생기면 사람이 승인한다. 그러니 여기는 매핑. 목록에 없는 값이 들어오면 그 자체가 사고 신호가 되고.
반대로 캠페인 이름은 계속 늘어난다. 매달 새로 생기고 상품마다 다르니 목록으로 관리할 수가 없지. 그러니 여기는 파싱. 값 안에 구조를 심어 두고 나중에 쪼개 읽는 방식.
밑줄과 하이픈에 서로 다른 일을 시키면, 나중에 쪼개 읽는 쿼리가 안 깨진다.
구분자를 두 종류로 나누는 건 사소해 보이지만 효과가 크다. 밑줄은 utm_campaign에서만 구조 구분자로 쓰고, 나머지 자리에서는 하이픈으로 단어를 잇는다. 그러면 SPLIT(campaign, '_')이 항상 세 조각을 돌려주니까 파싱이 안 흔들린다.
한글은 값에 넣지 말고 매핑표에 둔다
한글을 UTM 값에 넣으면 URL에서 퍼센트 인코딩으로 변한다. 리포트에는 알아볼 수 없는 문자열이 뜨고, 어느 단계에서 디코딩되느냐에 따라 같은 값이 두 모양으로 갈리기도 하지.
그래서 값은 영문 소문자로 통일하고, 사람이 읽을 한글 이름은 별도 매핑표에 둔다. signup이 무슨 상품인지는 표를 보면 되고, 리포트는 표를 조인해서 한글로 보여주면 된다.
정리한 컨벤션은 대략 이런 모양. 매체를 유형으로 일반화한 버전이고, 대괄호는 채워 넣는 자리다.
| 매체 유형 | utm_source | utm_medium | utm_campaign | channel_group |
|---|---|---|---|---|
| 공식 블로그 | naver | blog | official_content_[yyyymm] | referral |
| 공식 커뮤니티 | naver | cafe | official_community_[yyyymm] | referral |
| 상위노출 대행 | naver | blog-top | agency_top_[yyyymm] | referral |
| 인플루언서 제휴 | naver | blog-influencer | [creator]_promo_[yyyymm] | affiliate |
| 검색광고 | naver | cpc | [product]_lead_[yyyymm] | paid_search |
| 메시지 채널 발송 | kakao | message | official_push_[yyyymm] | social |
| 크리에이터 PPL | youtube | ppl | [creator]_ppl_[yyyymm] | video |
| 메타 광고 | {{site_source_name}} | da | {{campaign.name}} | paid_social |
| 성과형 제휴 | [매체코드] | cpa | [매체코드]_cpa_[yyyymm] | affiliate |
utm_content는 표에서 뺐는데, 규칙은 한 줄이면 충분해서다. 소재나 게시물을 하나로 지목하는 식별값을 하이픈으로 이어 적는다. 게시물이면 blog-[게시물id], 광고 소재면 광고 ID. 이 값이 나중에 링크 하나를 되짚는 열쇠가 된다.
매체가 채워 넣는 값은 우리 규칙을 모른다
여기까지는 우리가 손으로 적는 링크 얘기였고. 광고 매체는 사정이 다르지. 값을 우리가 아니라 매체가 채운다.
메타는 중괄호 두 개짜리 자리표시자를 준다. {{campaign.name}}이나 {{ad.name}}을 링크에 넣어 두면 클릭 시점에 실제 이름으로 바뀌어 나가는 방식. 구글 애즈에도 {keyword}나 {creative} 같은 ValueTrack 파라미터가 있고.
편해 보이지만 함정이 셋.
함정 1, 이름은 최초 게재 시점에 굳는다
메타 공식 문서에 명시돼 있다. 이름 기반 파라미터는 광고가 최초로 게재될 때 사용된 캠페인·광고 세트·광고 이름으로 설정되고, 그 뒤에 이름을 바꿔도 파라미터 값은 원래 이름을 계속 참조한다.
즉 캠페인 이름을 컨벤션에 맞게 나중에 고쳐도 UTM에는 옛 이름이 계속 실린다. 값을 바꾸려면 캠페인을 새로 만들어 게시하는 수밖에 없고. 그래서 메타는 이름을 짓는 순간이 곧 컨벤션을 지키는 유일한 기회다.
같은 문서에 붙은 예시가 문제를 그대로 보여준다. 캠페인 이름이 Prospecting 2026이면 URL에는 utm_campaign=Prospecting%202026으로 들어간다.
공백이 %20이 되고 대문자가 그대로 남지. 밑줄 세 토큰 규칙은 여기서 이미 깨져 있다.
함정 2, 노출 위치에 따라 source가 다섯 갈래로 갈린다
{{site_source_name}}이 돌려주는 값은 다섯 개다. Audience Network는 an, Facebook은 fb, Instagram은 ig, Messenger는 msg, Threads는 th.
그런데 구글의 사이트 목록에는 fb와 ig만 소셜로 올라 있다. an과 msg와 th는 목록에 없고. 앞의 표에서 메타 광고 한 줄이 두 채널로 갈렸던 이유가 이것이다.
같은 예산으로 돌린 한 캠페인이 리포트에서 두 채널로 흩어진다.
해법은 source를 손보는 게 아니라 medium을 유료 정규식에 걸리는 값으로 바꾸는 것이다. da 대신 cpc나 paid-social처럼 .*cp.* 또는 paid.*에 걸리는 값을 쓰면, 목록에 없는 source라도 유료 계열로 모인다.
함정 3, 자동 태깅이 켜져 있으면 UTM이 진다
구글 애즈에는 함정이 하나 더 있지. 애널리틱스 문서가 이렇게 적어 뒀다. 수동 태깅과 자동 태깅을 같이 쓰면 source·medium 같은 트래픽 분류 차원은 자동 태깅 값을 쓴다.
그러니까 링크에 UTM을 정성껏 붙여도 gclid가 함께 있으면 채널 분류는 UTM이 아니라 클릭 ID를 따라간다. 반대로 클릭 ID를 의도대로 쓸 수 없는 상황에서는 UTM 파라미터 하나만 있어도 모든 값을 UTM에서 끌어온다고 같은 문서가 명시하고.
실무적으로 중요한 건 그다음 문장이다. UTM을 하나라도 붙일 거면 관련 파라미터를 전부 붙이라고 권고하는데, 빠진 파라미터는 리포트에서 (not set)으로 남기 때문이다. 절반만 붙인 링크가 제일 나쁘다는 뜻이지.
ValueTrack에도 비슷한 구멍이 있다. {keyword}는 검색 네트워크에서는 매칭된 키워드를 돌려주지만, 동적 검색광고나 실적 최대화처럼 키워드 없이 매칭되는 캠페인에서는 빈 값을 돌려준다.
컨벤션 표에 utm_term={keyword}라고 적어 뒀는데 그 자리가 비어서 나가는 것. 값이 틀린 게 아니라 아예 없는 셈이라 리포트에서는 조용히 사라진다.
오프페이지 링크는 UTM만으로 안 갈린다
이미지: 링크가 퍼지는 순간, 꼬리표는 원래 자리를 기억하지 못한다.
여기서 앞 편에서 다룬 추적 링크 얘기로 넘어간다. 우리 사이트 밖에 심는 링크는 UTM만으로 관리가 안 되거든.
이유는 둘. 하나는 UTM이 도착지에서만 읽힌다는 것. 사이트 밖 링크의 클릭은 도착 전에 일어나고, 그 클릭을 세려면 리다이렉트 도메인을 한 번 거치게 해야 한다. 앞 편에서 go. 같은 서브도메인을 쓴 이유가 그것이었지.
다른 하나는 같은 링크가 복사돼 퍼진다는 것이다. 커뮤니티에 올린 링크는 캡처되고 인용되고 다른 방으로 옮겨진다. UTM은 그 여정 내내 처음 값 그대로 따라다니니까, 리포트에는 여전히 처음 심은 자리로 찍힌다.
그래서 리다이렉트 링크에는 UTM과 별개의 이름 규칙이 필요하다. 원칙은 하나면 충분하고.
- 링크 ID는 "심은 자리" 단위로 발급하라: 같은 콘텐츠라도 심은 곳이 다르면 다른 링크다. 콘텐츠 하나에 링크 하나가 아니라, 배치 하나에 링크 하나.
- 경로에 매체 유형과 일련번호만 담아라:
go.example.com/c/blog-014정도. 파트너 이름을 URL에 박으면 밖으로 노출되니 코드로 두고 표에서 푼다. - 같은 값을 utm_content에 복사하라: 리다이렉트 서버 로그의 링크 ID와 GA4의
utm_content가 같은 값이어야, 클릭 수와 도착 수를 한 줄에 놓을 수 있다.
세 번째가 핵심이지. 두 시스템이 각자 다른 키를 쓰면 조인이 안 되고, 그러면 "링크는 눌렸는데 세션이 없다"는 차이를 영영 설명하지 못한다.
BigQuery로 어긴 값을 잡는다
컨벤션 문서는 지켜지지 않는다. 담당자가 바뀌고 급한 캠페인이 끼어들면 어김없이 새 값이 들어오지. 그러니 문서를 늘리는 대신 어긴 값을 주기적으로 꺼내 보여주는 쿼리를 두는 편이 낫다.
GA4를 BigQuery로 내보내고 있다면 재료는 이미 있다. 다만 시작하기 전에 함정 하나를 먼저 치워야 한다.
UTM처럼 보이는 필드가 셋이다
GA4 내보내기 스키마에는 트래픽 소스를 담은 필드가 세 갈래로 있고, 셋 다 이름이 비슷하다. 잘못 고르면 쿼리는 잘 도는데 답이 틀린다.
컨벤션이 지켜졌는지 보는 것은 가운데 필드다. 위 필드로 세면 신규 사용자만 세게 된다.
아래 네 쿼리는 구조를 보여주려고 쓴 예시이고, 실행 결과를 옮긴 것이 아니다. 데이터셋 이름과 기간은 각자 환경에 맞게 바꿔야 한다.
쿼리 1, 값이 어느 이벤트에 실려 있는지부터 센다
UTM이 어떤 이벤트에 붙어 있는지는 속성마다 다르다. 짐작하지 말고 세어 보는 게 빠르지.
-- 예시 쿼리다. 실행하지 않았고 결과 숫자도 없다.
-- 어느 이벤트에 수집된 UTM이 실려 있는지 먼저 확인한다.
SELECT
event_name,
COUNT(*) AS events,
COUNTIF(collected_traffic_source.manual_source IS NOT NULL) AS with_utm
FROM `프로젝트.analytics_XXXXXXXXX.events_*`
WHERE _TABLE_SUFFIX BETWEEN '20260701' AND '20260731'
GROUP BY event_name
ORDER BY with_utm DESC
LIMIT 20;
보통은 session_start와 첫 page_view에 몰려 있다. 세션 단위로 세고 싶다면 아래 쿼리들의 event_name을 그 값으로 좁히면 되고, 반대로 전체 이벤트로 세면 같은 세션이 여러 번 잡혀 비율이 왜곡된다.
쿼리 2, 표기 변종을 한 묶음으로 모은다
쪼개짐의 가장 흔한 형태는 같은 뜻인데 표기가 다른 값. 소문자로 눌러서 묶어 보면 바로 드러난다.
-- 예시 쿼리다. 결과 숫자는 없다.
-- 소문자로 정규화했을 때 원본이 둘 이상이면 표기 변종이다.
WITH s AS (
SELECT
LOWER(TRIM(collected_traffic_source.manual_source)) AS norm_source,
collected_traffic_source.manual_source AS raw_source
FROM `프로젝트.analytics_XXXXXXXXX.events_*`
WHERE _TABLE_SUFFIX BETWEEN '20260701' AND '20260731'
AND event_name = 'session_start'
AND collected_traffic_source.manual_source IS NOT NULL
)
SELECT
norm_source,
COUNT(DISTINCT raw_source) AS variants,
ARRAY_AGG(DISTINCT raw_source ORDER BY raw_source LIMIT 10) AS samples
FROM s
GROUP BY norm_source
HAVING variants > 1
ORDER BY variants DESC;
이 쿼리가 naver 하나에 Naver와 naver.com을 함께 묶어 돌려준다면 그게 쪼개짐이다. 다만 거기서 갈리는 건 리포트 줄까지고, naver와 naver.com은 둘 다 목록에 검색 사이트로 올라 있어 채널 그룹은 같은 자리에 떨어진다.
채널 그룹까지 옮겨 버리는 변종은 따로 있지. 목록에는 naver가 검색으로, blog.naver.com이 소셜로 각각 등록돼 있다. 같은 블로그 유입인데 source를 도메인으로 적었느냐 아니냐에 따라 Organic Search와 Organic Social로 갈린다는 뜻. 표기 하나가 리포트 줄만 나누는 게 아니라 채널을 통째로 옮기는 경우.
쿼리 3, 목록 밖 값과 구조 위반을 함께 잡는다
매핑 대상인 medium은 허용 목록과 대조하고, 파싱 대상인 campaign은 토큰 수와 금지 문자를 본다. 한 번에 보면 편하지.
-- 예시 쿼리다. 결과 숫자는 없다.
-- 허용 목록 밖 medium + 캠페인 구조 위반을 한 번에 뽑는다.
WITH allowed AS (
SELECT medium FROM UNNEST([
'blog','cafe','kin','blog-top','kin-top','cafe-affiliate',
'blog-influencer','message','video','ppl','cpc','bs','da','sns','cpa','display'
]) AS medium
),
s AS (
SELECT
collected_traffic_source.manual_medium AS medium,
collected_traffic_source.manual_campaign_name AS campaign
FROM `프로젝트.analytics_XXXXXXXXX.events_*`
WHERE _TABLE_SUFFIX BETWEEN '20260701' AND '20260731'
AND event_name = 'session_start'
)
SELECT
medium,
campaign,
COUNT(*) AS sessions,
medium IS NULL OR medium NOT IN (SELECT medium FROM allowed) AS unknown_medium,
ARRAY_LENGTH(SPLIT(IFNULL(campaign, ''), '_')) <> 3 AS bad_token_count,
REGEXP_CONTAINS(IFNULL(campaign, ''), r'[^a-z0-9_\-]') AS bad_charset
FROM s
GROUP BY medium, campaign
HAVING unknown_medium OR bad_token_count OR bad_charset
ORDER BY sessions DESC;
bad_charset 열이 앞에서 본 메타 함정을 그대로 잡는다. 대문자와 %20이 섞인 캠페인 이름이 여기 걸려 나올 테니까. 한글이 인코딩돼 들어온 값도 같은 조건에 걸리고.
쿼리 4, 우리 분류와 도구 분류가 어긋난 곳을 센다
마지막은 이 글의 주제를 그대로 쿼리로 옮긴 것. 우리 매핑표를 붙여 놓고, 우리가 유료라고 부르는 유입이 GA4 기준으로도 유료 계열인지 본다.
-- 예시 쿼리다. 결과 숫자는 없다.
-- 우리 channel_group 과 GA4 유료 판정 규칙이 어긋나는 조합을 센다.
WITH mapping AS (
SELECT * FROM UNNEST([
STRUCT('blog' AS medium, 'referral' AS channel_group),
STRUCT('blog-top', 'referral'),
STRUCT('bs', 'paid_search'),
STRUCT('cpc', 'paid_search'),
STRUCT('da', 'paid_social'),
STRUCT('sns', 'paid_social'),
STRUCT('cpa', 'affiliate')
])
),
s AS (
SELECT
collected_traffic_source.manual_source AS source,
collected_traffic_source.manual_medium AS medium,
COUNT(*) AS sessions
FROM `프로젝트.analytics_XXXXXXXXX.events_*`
WHERE _TABLE_SUFFIX BETWEEN '20260701' AND '20260731'
AND event_name = 'session_start'
GROUP BY source, medium
)
SELECT
s.source,
s.medium,
m.channel_group AS our_label,
REGEXP_CONTAINS(IFNULL(s.medium, ''),
r'^(.*cp.*|ppc|retargeting|paid.*)$') AS ga4_paid_rule_hit,
s.sessions
FROM s
LEFT JOIN mapping m USING (medium)
WHERE STARTS_WITH(IFNULL(m.channel_group, ''), 'paid')
AND NOT REGEXP_CONTAINS(IFNULL(s.medium, ''),
r'^(.*cp.*|ppc|retargeting|paid.*)$')
ORDER BY s.sessions DESC;
이 쿼리에 줄이 뜨면, 우리는 유료라고 부르는데 GA4는 유료로 안 세는 유입이 있다는 뜻이다. bs와 da와 sns가 여기 걸려 나오지. 광고비를 쓴 채널이 리포트에서 오가닉으로 앉아 있는 상태.
고치는 방법은 둘 중 하나. medium을 유료 정규식에 걸리는 값으로 바꾸거나, GA4에 맞춤 채널 그룹을 만들어 우리 매핑을 도구 안으로 옮기거나.
앞의 방법은 오늘 이후 데이터만 바뀌고 과거는 그대로다. 어느 쪽을 고르든 바뀐 시점을 리포트에 표시해 두는 일이 함께 가야 하고.
네 쿼리는 분기마다 손으로 도는 것보다 예약 쿼리로 걸어 두는 편이 낫다. 결과가 0줄이 아니면 알림이 가게 해 두면 컨벤션이 문서가 아니라 장치가 된다.
정직하게, 여기까지가 한계다
이 글에서 확인한 것과 확인하지 못한 것을 갈라 둔다.
- 공식 문서로 확인한 것: 기본 채널 그룹의 판정 조건과 정규식, 사이트 목록의 내용, 내보내기 스키마의 세 필드 설명, 메타 동적 파라미터의 값 다섯 개와 이름 고정 동작, ValueTrack의 빈 값 조건, 자동 태깅 우선 규칙. 본문 링크와 아래 출처가 전부 원문이다.
- 규칙에 대입해 추론한 것: 앞의 대조표에 적은 GA4 채널 판정 결과다. 실제 속성에서 리포트를 열어 확인한 값이 아니라, 공개된 조건에 우리 값을 넣어 본 결과다. 조건을 만족하는 규칙이 하나뿐인 조합만 골랐지만 그래도 추론은 추론이다.
- 실행하지 않은 것: 본문의 BigQuery 쿼리 넷은 구조를 보이려고 쓴 예시다. 어떤 데이터셋에서도 돌린 적이 없고, 그래서 결과 숫자도 이 글에 없다. 문법과 필드명은 스키마 문서 기준으로 맞췄지만 각자 환경에서 한 번은 실행해 봐야 한다.
- 바뀔 수 있는 것: 사이트 목록은 구글이 갱신하고, 채널 규칙도 개정된다. 매체의 동적 파라미터 사양도 마찬가지고. 위 내용은 2026년 8월 확인 기준이다.
그리고 컨벤션이 원리상 못 막는 것도 하나 있다. 링크를 만든 사람이 표를 안 보는 경우. 검증 쿼리는 어긴 값을 사후에 알려줄 뿐이라, 링크를 만드는 화면 자체에 규칙을 심는 편이 훨씬 낫다. 생성기를 하나 두고 손으로 붙이는 링크를 금지하는 쪽이지.
오늘 30분이면 우리 라벨 상태를 알 수 있다
표를 새로 만들 필요는 없고. 지금 들어오고 있는 값이 무엇인지부터 보면 된다.
- 변종부터 세라: 지난달 source와 medium을 소문자로 눌러 묶어라. 원본이 둘 이상인 묶음이 나오면 그 수가 지금 쪼개진 정도다.
- 목록을 확인하라: 우리가 쓰는 source가 구글의 사이트 목록에 있는지, 있다면 어느 갈래인지 보라. 목록에 있으면 medium과 무관하게 그 갈래로 끌려간다.
- 유료를 대조하라: 우리가 유료라고 부르는 medium이
^(.*cp.*|ppc|retargeting|paid.*)$에 걸리는지 하나씩 대 보라. 안 걸리는 값이 광고비가 오가닉으로 새는 지점이다. - 매체가 채우는 자리를 열어 보라: 메타 캠페인 이름에 공백과 대문자가 있는지, 실적 최대화의
{keyword}가 비어 오는지 확인하라. 이름은 최초 게재 뒤에 못 바꾼다. - 쿼리를 예약으로 걸어라: 위 검증을 주 1회 돌리고 0줄이 아니면 알림이 가게 하라. 문서는 잊히지만 알림은 안 잊힌다.
그래도 하나만 챙긴다면, 이것이다.
컨벤션은 값을 정하는 일이 아니라, 그 값을 누가 어떻게 읽는지까지 정하는 일이다.
이 글은 디지털 마케팅 분석 입문 시리즈의 심화편이다. 사이트 밖 링크를 재는 구조는 오프페이지 측정, 추적 링크와 MMP에서 다뤘고, UTM 자체가 처음이면 UTM 완전 기초부터 보는 편이 낫다. 링크가 아니라 사이트 안 이벤트의 이름 체계는 이벤트 택소노미 설계법으로 이어지고, 전체 지도는 측정 설계 다섯 층 진단에 있다.
근거·출처
- 기본 채널 그룹의 판정 조건과 medium 정규식, 사이트 목록 다운로드: Analytics Help, Default channel group
- 내보내기 스키마의 traffic_source·collected_traffic_source·session_traffic_source_last_click 정의: Analytics Help, BigQuery Export schema
- 자동 태깅과 수동 태깅을 함께 쓸 때의 우선순위, 파라미터 누락 시 (not set): Analytics Help, Traffic-source dimensions, manual tagging, and auto-tagging
- 메타 동적 URL 매개변수, site_source_name 값 다섯 개와 이름 기반 파라미터 고정 동작: Meta 비즈니스 지원 센터, URL 다이내믹 매개변수 사양
- ValueTrack의 {keyword}가 동적 검색광고·실적 최대화에서 빈 값을 반환: Google Ads Help, ValueTrack parameters
본문의 BigQuery 쿼리는 실행하지 않은 예시이며 결과 수치를 담고 있지 않다. GA4 채널 판정 결과는 공개된 규칙에 값을 대입한 추론이고 실제 속성 확인값이 아니다. 사이트 목록과 채널 규칙, 매체의 파라미터 사양은 개정될 수 있어 위 내용은 2026년 8월 확인 기준이다. 컨벤션 예시의 매체와 파트너는 유형으로 일반화했다.
이 글이 도움이 되었다면 공유해주세요
최신 인사이트
카카오톡 공유 썸네일 안 나올 때 해결법 (Kakao JavaScript SDK 2025)
카카오톡 공유하기에서 썸네일 이미지가 안 나오는 원인과 scrapImage로 KAGE URL을 변환해 해결하는 방법을 단계별로 설명합니다. 지금 확인하세요.
Claude Code 서브에이전트로 CRM 사용자 설명서 20분 만에 완성하는 방법
Claude Code 서브에이전트를 활용해 CRM 코드베이스에서 사용자 설명서와 비주얼 가이드북을 20분 만에 자동 생성한 실전 사례를 공유합니다. 지금 확인하세요.
GEO 생성형 엔진 최적화: 2025년 AI 마케팅 전략 완전 가이드
GEO 생성형 엔진 최적화란 무엇이며 기존 SEO와 어떻게 다른지, 2025년 AI 마케팅 전략의 핵심 실행 방법 5가지를 데이터와 함께 알아보세요.
관련 프로젝트
새 글을 이메일로 받아보세요
마케팅, 분석, 개발 인사이트를 정리해서 보내드려요.

