랜딩페이지 전환은 CTA 버튼을 누른 순간이 아니라 서버가 문의를 정상 접수한 순간으로 측정해야 합니다. 클릭, 제출 시도, 성공, 실패를 하나의 이벤트로 묶으면 실제 문의 수를 부풀리고 폼 장애도 찾기 어렵습니다.
가장 작은 실무 퍼널은 page_view → contact_click → submit_intent → submit_success입니다.
실패는 성공에 섞지 않고 submit_failure로 따로 봅니다.
네 단계의 의미를 먼저 정합니다
| 단계 | 예시 이벤트 | 발생 조건 | 주요 전환인가 |
|---|---|---|---|
| 방문 | page_view |
랜딩페이지가 표시됨 | 아니요 |
| 관심 | contact_click |
상담·전화·메일 CTA를 누름 | 아니요 |
| 제출 의도 | contact_form_submit_intent |
유효한 폼이 서버로 전송되기 직전 | 아니요 |
| 접수 성공 | contact_form_submit_success |
정상 입력에 대한 공급자 수락을 서버가 확인하고 성공 응답 | 예, 허니팟·QA 제외 |
| 실패·미확인 | contact_form_submit_failure |
요청 시작 뒤 서버 거절 또는 브라우저 통신 오류 | 아니요 |
GA4의 권장 이벤트 문서는 폼이나 정보 요청을 제출했을 때 generate_lead 사용을 권장합니다. 이 의미가
조직의 보고 기준과 맞을 때 사용하세요. 이 사이트에서 문의 완료는 서버가 접수를
확인한 성공을 뜻합니다. 전송을 시도했지만 저장이나 전달에 실패한 요청은 문의가
아닙니다. 다만 응답이 유실된 요청은 전달 여부가 미확인이므로 실패로 단정하지 않습니다. 서버 확인 뒤 generate_lead를 보내거나, 기존 성공 이벤트를 유지하고 두
이름의 관계를 문서로 남기세요.
이벤트 → 주요 이벤트 → Google Ads 전환
세 단계는 서로 다른 설정입니다. Google의 전환 생성·관리 안내도 이벤트, 주요 이벤트, 광고 전환을 나눕니다.
- 이벤트: 서버가 결과를 확인한 뒤 사이트가
contact_form_submit_success를 보냅니다. - 주요 이벤트: GA4에서 이 이벤트를 사업상 중요한 행동으로 표시합니다.
- Google Ads 전환: GA4와 Google Ads가 연결되어 있다면 이 주요 이벤트를 광고 측정과, 설정에 따라 입찰에 사용하는 전환으로 만들거나 가져옵니다.
세 단계에서 같은 서버 확인 결과를 사용하세요. 수치가 더 많이 쌓인다는 이유로 CTA 클릭을 주요 이벤트나 광고 전환으로 올리면 실제 문의보다 클릭을 최적화하게 됩니다. 다만 기여 분석, 집계 기간, 시간대와 설정 차이로 GA4와 Ads 합계는 달라질 수 있으므로 어떤 이벤트와 설정을 썼는지 함께 기록합니다.
클릭을 전환으로 잡으면 무엇이 틀어지나요?
사용자는 버튼을 누른 뒤 다음 상황에서 문의를 완료하지 못할 수 있습니다.
- 필수 입력 오류를 만난다.
- 네트워크가 끊긴다.
- 서버가 오류를 반환한다.
- 메일 발송이나 저장이 실패한다.
- 중복 클릭으로 요청이 여러 번 발생한다.
클릭만 보고하면 광고와 콘텐츠가 성공한 것처럼 보이지만 실제 담당자에게 도착한 문의는 없습니다. 클릭은 카피와 CTA 위치를 진단하는 지표로 남기고, 성공 이벤트만 key event로 설정합니다.
계산 예시: 클릭을 세면 보고서에 무엇이 찍히나
랜딩페이지 하나를 한 달 동안 운영해 랜딩 세션 1,000회가 나왔다고 해 봅시다. 숫자는 설명용입니다.
설명용 이벤트 횟수: 클릭 120회, 확인된 성공 38회
contact_click120contact_form_submit_intent45contact_form_submit_success38contact_form_submit_failure7contact_click을 주요 이벤트로 잡았다면 보고서에는 리드가 120건으로 찍힙니다.
이 예시의 확인된 성공 응답은 38회입니다. 클릭은 세션당 0.12회, 제출 시도는 세션당
0.045회입니다. 고유 세션을 세지 않았으므로 세션 전환율이라고 부를 수 없습니다.
모든 시도의 종료가 관측됐다는 가정에서 성공 비율은 38/45 = 84.4%, 실패·미확인
종료 비율은 7/45 = 15.6%입니다. 재시도는 새 시도로 세며, 같은 사람의 성공이 여러
번 들어갈 수 있습니다. 미확인 전달과 확정 거절을 서버 기록으로 구분하기 전에는
7회 모두를 유실된 문의라고 해석하지 않습니다.
코드가 실제로 무엇을 보장하는지(2026-09-27 소스 검토)
2026-09-27에 소스를 읽고 확인한 동작을 정리합니다. 아래 의사코드는 소스에서 요약·축약한 것이며, 비밀값이나 폼 입력 필드는 분석으로 보내지 않습니다.
// 브라우저 코드의 제어 흐름을 축약한 예시입니다.
// requestBody는 문의 API에만 보냅니다. track에는 입력값을 넣지 않습니다.
if (submitting || !form.reportValidity()) return;
submitting = true;
track("contact_form_submit_intent");
try {
const response = await fetch("/api/contact", {
method: "POST", body: requestBody
});
if (!response.ok) throw new Error("Request rejected");
track("contact_form_submit_success");
} catch {
// 서버 거절과 응답 유실 모두 여기에 들어올 수 있습니다.
track("contact_form_submit_failure");
} finally {
submitting = false;
}
// Worker: 허니팟은 먼저 조용히 성공 처리. 정상 입력은 검증 후
// Discord에 wait=true로 전송하고, 공급자의 비-2xx 응답/통신 오류는 502.
// 공급자 수락 뒤에만 정상 요청에 2xx를 반환합니다.
여기서 분명히 해 둘 경계가 있습니다.
- 허니팟은 의도적으로 성공처럼 응답합니다. 이 2xx는 리드가 저장·전달되었다는 증거가 아닙니다.
- 공급자 접수(2xx) ≠ 사람이 읽음. 이 백엔드에는 지속 데이터베이스나 멱등성 보장이 없습니다.
- 클라이언트의
submitting가드는 그 페이지 인스턴스 안의 동시 제출만 막습니다. 응답이 유실되면 재시도로 상류에 중복이 생길 수 있습니다. - 네트워크 타임아웃은 전달 미확인이지 서버 거절이 증명된 것이 아닙니다.
수치 정의를 고정합니다
완전 관측을 가정한 설명용 코호트에서 유효 시도 45건 = 확인된 성공 38건 + 실패·미확인 종료 7건입니다. 같은 사용자가 재시도할 수 있으므로 이 45는 45명이 아닙니다. 대기·취소 상태는 별도 상태로 두고, 완료되지 않은 코호트를 억지로 합계 100%로 맞추지 않습니다.
비율은 이렇게 계산합니다.
- 세션당 클릭 횟수 =
contact_click / landing sessions - 완료율 =
submit_success / submit_intent - 실패·미확인 종료 비율 =
submit_failure / submit_intent
클릭 120회 / 세션 1,000회 = 0.12 clicks/session이며, 이는 세션 CTR이 아닙니다. 세션 CTR을 구하려면 클릭이 발생한 고유 세션 수를 세어 1,000으로 나눠야 합니다. 하나의 세션에서 버튼을 여러 번 누른 경우가 섞이면 두 값은 달라집니다.
이벤트 계약서의 verified_on은 실제로 검증하기 전까지 비워 둡니다. 공급자 접수와 사람의 확인, 이벤트 횟수와 고유 세션 수를 같은 수치로 취급하지 않습니다.
이 절의 근거는 GA4 이벤트 참조와 위 소스 검토입니다. GA4 실시간 수집과 운영자의 실제 확인은 검증 범위에 포함되지 않습니다.
구현 순서
1. 공통 문맥을 작게 정하기
locale, page_type, content_group, ui_location, contact_method처럼 실제 보고에
쓸 값만 보냅니다. 값의 종류가 계속 늘어나는 버튼 문구나 원문은 custom dimension으로
만들지 않습니다.
gtag("event", "contact_click", {
locale: "ko",
page_type: "landing_page",
ui_location: "hero",
contact_method: "form"
});
2. 유효한 제출 직전에 의도 기록하기
브라우저 validation을 통과하고 실제 request를 시작할 때 한 번 기록합니다. 단순히 submit button에 click listener만 붙이면 keyboard 제출과 validation 실패를 정확히 구분하기 어렵습니다.
3. 서버 응답 뒤 성공과 실패 나누기
성공 응답을 받은 뒤 contact_form_submit_success를 보냅니다. 실패 응답과 exception은
contact_form_submit_failure에 result처럼 제한된 오류 종류만 붙입니다. 오류 원문,
폼 입력값, 이메일 주소는 보내지 않습니다.
4. 중복을 막기
제출 중 버튼과 핸들러를 잠급니다. 서버 멱등성은 별도로 설계해야 하며, 현재 IILL 문의 백엔드에는 구현되어 있지 않습니다. GA4 이벤트도 같은 성공을 두 번 보내지 않도록 상태를 관리합니다. thank-you page를 함께 쓰는 경우, callback과 page_view 양쪽에서 성공 이벤트가 중복되지 않는지 확인합니다.
UTM은 유입 이름표이지 사용자 데이터 칸이 아닙니다
외부 캠페인 링크에는 합의된 utm_source, utm_medium, utm_campaign을 사용합니다.
내부 링크에 UTM을 붙이면 기존 유입 세션을 새 캠페인처럼 덮어쓸 수 있으므로 사용하지
않습니다. 임의 query 전체를 분석으로 보내지 말고 허용한 값만 정규화합니다.
예시 규칙:
utm_source:naver,google,newsletterutm_medium:cpc,organic,emailutm_campaign: 팀이 합의한 짧은 campaign ID
이름, 이메일, 전화번호, 문의 내용, 내부 customer ID를 UTM이나 event parameter에 넣지 마세요. GA4는 상담 내용을 보관하는 CRM이 아닙니다.
사례: HTML2WEB은 결제 의향과 결제를 어떻게 나누나
IILL의 HTML 공유 도구 HTML2WEB은 결제에도 같은 원칙을 씁니다. 결제를 자체 데이터베이스에 네 단계로 기록합니다.
- 방문자가 요금제 버튼을 누르면
checkout_intent_clicked. 상품, 결제사, 버튼 위치, 결제가 열려 있었는지를 함께 남깁니다. - 결제 페이지가 만들어지면 결제 시작.
- 결제 페이지에서 돌아오면 복귀.
- 결제사 웹훅이 주문을 확인했을 때만 활성화.
매출로 세는 것은 활성화뿐입니다. 결제 페이지에서 돌아온 것만으로는 세지
않습니다. 결제하지 않고 돌아올 수도 있기 때문입니다. 결제를 열기 전에도 요금제
버튼은 실제처럼 작동했습니다. 누르면 결과값 unavailable과 함께
checkout_intent_clicked가 기록되고, 방문자의 언어로 결제 준비 중이라는 안내가
떴습니다. 클릭을 매출로 부르지 않으면서 수요를 잰 방법입니다. QA로 표시된 트래픽은
의사결정 보고서에서 빠집니다.
템플릿: 주요 이벤트마다 계약서 한 장
승격하는 이벤트마다 짧은 계약서를 하나씩 둡니다. 폼이나 서버 응답이 바뀔 때마다 다시 검토하세요.
event: contact_form_submit_success
fires_when: 서버가 문의 저장 또는 전달을 확인했을 때
key_event: true
ads_conversion: GA4에서 가져옴 # 또는: 없음
parameters:
locale: [en, ko, ja]
page_type: [landing_page, service, contact]
contact_method: [form]
never_send: [name, email, phone, message, raw_error, free_text_query]
duplicates: 서버 결과 한 번에 이벤트 한 번, 전송 중에는 제출 버튼 비활성화
qa: 공개 전에 내부 트래픽 표시
owner: 웹 담당
verified_on: null # 설명용 빈 양식. 실제 검증한 날에만 기록
공개 전 QA
- 별도 QA browser에 테스트 표시를 붙입니다.
- CTA 클릭, 유효한 제출, 성공, 실패를 한 번씩 실행합니다.
- browser payload에서 event name과 제한된 parameter를 확인합니다.
- GA4 Realtime 또는 DebugView에서 순서와 수신을 확인합니다.
- 서버의 실제 접수·메일 발송도 함께 확인합니다.
- QA traffic은 분석에서 구분하되 raw collection을 바로 삭제하지 않습니다.
Google의 lead form 보고 가이드는 방문부터 제출까지의 이탈을 funnel exploration으로 보는 방법을 설명합니다. custom dimension은 parameter가 실제로 수집되고 보고에 필요하다는 것을 확인한 뒤 만드세요. 새 정의는 과거 데이터에 소급 적용되지 않습니다.
보고서에서 볼 네 가지 비율
- 세션당 CTA 클릭 횟수 =
contact_click / landing sessions - 세션당 제출 시도 횟수 =
submit_intent / landing sessions - 완료율 =
submit_success / submit_intent - 실패·미확인 종료 비율 =
submit_failure / submit_intent
처음에는 locale, landing page, source/medium 정도로만 나눕니다. 표본이 작은데 버튼별, 기기별, 캠페인별로 모두 쪼개면 결론보다 노이즈가 커집니다. 28일 단위로 보되, 폼 실패는 운영 장애이므로 즉시 확인합니다.
지금 할 일: 실제 테스트 문의를 한 건 보내고 브라우저 전송값, GA4 DebugView, 문의가 남은 메일함이나 데이터베이스 기록까지 이어서 확인하세요. Ads가 연결되어 있다면 같은 이벤트가 의도한 전환에 들어왔는지도 봅니다. 홈페이지 전체가 필요한지 먼저 판단해야 한다면 홈페이지와 랜딩페이지의 차이를 확인하고, 폼과 측정 기준을 함께 설계하려면 랜딩페이지 제작 서비스에서 범위를 정할 수 있습니다.