웹 SDK - Google 태그 관리자 구현 가이드

문서

개요

INFO: 웹 어트리뷰션은 엔터프라이즈 기능입니다. 계정에 이 기능을 활성화하려면 고객 성공 매니저에게 문의하십시오.

이 가이드는 Google 태그 관리자 (GTM)를 사용하여 Singular 웹 SDK를 구현하는 방법을 설명합니다. 이 방법은 웹사이트 코드에 직접 접근할 수 없는 팀이나 GTM을 통해 추적을 관리하려는 팀에 이상적입니다.

중요! 동일 사이트에서 GTM과 네이티브 자바Script 구현을 동시에 사용하지 마십시오. 중복 추적 및 부풀려진 이벤트 수를 방지하려면 하나의 방법만 선택하십시오. Singular는 자동으로 이벤트 중복 제거를 수행하지 않습니다.

  • 네이티브 자바Script와 Google 태그 관리자 방식을 동시에 구현하지 마십시오. 중복 추적을 방지하려면 한 가지 방법만 선택하십시오.
  • Singular 웹 SDK는 사용자의 브라우저에서 클라이언트 측으로 실행되도록 설계되었습니다. 정상적인 작동을 위해서는 localStorageDOM(Document Object Model) 과 같은 브라우저 기능에 대한 접근이 필요합니다. 서버 측 (예: Next.js SSR 또는 node.js를 통한)에서 SDK를 실행하려고 시도하지 마십시오. 서버 환경은 브라우저 API에 대한 접근을 제공하지 않으므로 추적 오류가 발생합니다.

필수 조건

시작하기 전에 다음을 준비하십시오:

구현 단계


1단계: Singular 웹 추적 템플릿을 GTM 컨테이너에 추가하기

  1. Google 태그 관리자 계정에 로그인하여 웹사이트의 컨테이너를 선택하세요.
  2. 태그 > 새로 만들기로 이동합니다.
  3. 태그 이름 지정: "Singular 초기화 태그"
  4. 태그 구성 상자를 클릭하여 태그 설정을 시작합니다.
  5. 태그 유형 선택 : "커뮤니티 템플릿 갤러리에서 더 많은 태그 유형 찾기"를 선택합니다.
  6. "Singular" 를 검색하고 "Singular 웹 추적"을 선택하세요. "작업 공간에 추가" 버튼을 클릭하세요.

2단계: SDK 초기화

  1. 다음과 같이 양식 필드를 완성하세요:
    • 추적 유형 을 초기화로 설정하세요
    • Api 키 필드에 실제 SDK 키를 입력하세요
    • 실제 SDK 시크릿을 시크릿 필드에 입력하세요. 필드
    • 실제 제품 ID를 입력하세요. 형식은 com.website-name 과 같아야 하며, Singular 플랫폼의 앱 페이지에 있는 번들 ID 값과 일치해야 합니다.

      팁! 테스트용으로 별도의 제품 ID(예: com.website-name.dev )를 사용하세요. 본격적인 서비스 시작 전에는 반드시 업데이트해야 합니다. 이렇게 하면 Singular 리포팅에서 테스트 데이터와 본격 서비스 앱 데이터를 완전히 분리할 수 있습니다.

    • 선택 사항:
  2. 이 태그가 작동하도록 트리거를 구성하려면 트리거 설정을 선택하십시오.
  3. 새로 만들기를 선택하고 트리거 이름을"Singular Init Trigger" 로 지정하세요.
  4. 트리거 구성을 클릭하고 "페이지 뷰 - 창 로드됨"을 선택한 후 "저장"을 클릭하세요.
  5. 태그를 저장하려면 "저장"을 다시 클릭하세요.
  6. 태그 페이지에서 "미리 보기"를 클릭하고 Singular 초기화 태그가 트리거되는지 테스트합니다.

    singular_init_tag_fired.png

성공! 미리보기 콘솔의 '태그 발동' 섹션에 "Singular 초기화 태그"가 표시되면 초기화 태그 설정이 성공적으로 완료된 것입니다.

중요! SPA(Singular 페이지 애플리케이션) 의 경우 , 다른 페이지로 이동할 때마다 페이지 방문 트랙타입을 트리거해야 합니다. 첫 페이지 로드 시 페이지 방문을 호출하지 마십시오. 초기화가 이미 페이지 방문을 보고했기 때문입니다. 해결책 개요

솔루션 개요

  • 사용자 정의 JavaScript 변수를 사용하여 첫 페이지 로드인지 감지합니다.
  • 2개의 태그 구성:
    • "Singular 초기화 태그"(추적 유형 = 초기화) : 초기 페이지 로드 시에만 발동.
    • "Singular 페이지 방문 태그"(추적 유형 = 페이지 방문)는 역사 변경 트리거를 사용하여 경로 변경 시마다(초기 로드 제외) 발동됩니다.
  • SPA가 경로 변경 시 히스토리 이벤트를 데이터 레이어에 푸시하도록 설정하십시오.

image5.png


3단계: 이벤트 추적

SDK 초기화 후, 사용자가 웹사이트에서 중요한 행동을 수행할 때 커스텀 이벤트를 추적할 수 있습니다.

중요! Singular는 중복 이벤트를 차단하지 않습니다! 페이지 새로고침이나 중복 발생을 방지하는 보호 장치는 개발자의 책임입니다. 특히 구매 이벤트의 경우 오류가 있는 구매 데이터를 방지하기 위해 중복 제거 방법을 적용하는 것이 권장됩니다. 예시는 아래 "단계 5: 중복 이벤트 방지"를 참조하십시오.

Basic EventConversion EventRevenue Event

기본 이벤트 추적

단순한 이벤트를 추적하거나 유효한 JSON을 사용하여 사용자 정의 속성을 추가해 이벤트에 대한 추가 컨텍스트를 제공하세요:

  1. Singular 웹 추적 템플릿을 사용하여 사용자 정의 이벤트용 새 태그를 생성하세요.

  2. 이벤트 태그에 이름을 지정하세요.

  3. 추적 유형 = 사용자 정의 이벤트 선택

  4. "이벤트 이름" 필드를 조정하거나 적절한 변수를 설정하세요.

  5. "사용자 지정 ID" 필드를 조정하거나 적절한 변수를 설정하세요. 필요 시 "속성"을 조정하여 이벤트에 키/값 쌍을 전달하세요.

  6. 이벤트에 키/값 쌍을 전달하려면 원하는 경우 "속성"을 조정하십시오.

  7. 예상된 경우에만 태그가 발동되도록 트리거를 구성하십시오.

  8. 트리거와 태그를 저장하고 미리보기에서 테스트하세요.

custom_event.png

일반적인 이벤트 구현 패턴

Page Load EventsButton Click EventsForm Submission Events

페이지 로드 이벤트용 GTM 트리거 생성

Singular 웹 SDK를 Google 태그 관리자와 함께 구현하려면 페이지 로드 시 발동되는 페이지 로드 트리거를 생성해야 합니다.

빠른 설정: GTM에서 트리거 > 새로 만들기 > 트리거 구성으로 이동하여 트리거 유형으로 "페이지 뷰"를 선택하세요. 대부분의 구현에서는 모든 페이지 로드 시 트리거가 작동하도록 "모든 페이지 뷰"를 선택하세요.

전체 설정 안내: Google 공식 태그 관리자 문서를 참조하세요:


4단계: 고객 사용자 ID 설정

Singular SDK 메서드를 사용하여 내부 사용자 ID를 Singular로 전송할 수 있습니다.

참고: Singular의 크로스 디바이스 솔루션을 사용하는 경우 모든 플랫폼에서 사용자 ID를 수집해야 합니다. 참고: 여러 사용자가 Singular 기기를 사용하는 경우 로그인 및 로그아웃 시마다 사용자 ID를 설정 및 해제하는 로그아웃 흐름 구현을 권장합니다.

참고: 여러 사용자가 Singular 기기를 사용하는 경우, 로그인 및 로그아웃 시마다 사용자 ID를 설정하고 해제하는 로그아웃 흐름을 구현하는 것이 좋습니다.

팁! 모바일 SDK에서 사용하는 것과 동일한 고객 사용자 ID를 사용하세요. 이를 통해 크로스 디바이스 어트리뷰션이 가능해지며 플랫폼 전반에 걸친 사용자 행동에 대한 완전한 시각을 제공합니다.

사용자가 로그인하지 않은 상태에서 웹사이트에서 행동을 수행하는 한, 이벤트는 Singular에서 생성한 사용자 ID와 함께 Singular로 전송됩니다. 그러나 사용자가 등록하거나 로그인한 후에는, 웹사이트에서 사용하는 사용자 ID(예: 해시 처리된 이메일 주소)와 함께 이벤트를 Singular로 전송할 수 있습니다.

Singular는 사용자 수준 데이터 내보내기( 애트리뷰션 로그 내보내기 참조)와 내부 BI 포스트백(설정된 경우, 내부 BI 포스트백 구성 참조)에서 사용자 ID를 사용합니다.

사용자 ID를 Singular로 전송하는 방법은 두 가지입니다:

  • 권장: 웹사이트가 열릴 때 사용자 ID를 알고 있다면 , SDK 초기화 시 초기화 트랙 유형에서 사용자 ID를 설정하세요. 이렇게 하면 첫 페이지 방문부터 Singular가 사용자 ID를 사용할 수 있습니다. 또는, 인증이 발생한 후 언제든지(일반적으로 인증 후) 트랙 유형 = 로그인의 태그를 트리거할 수 있습니다. 사용자 ID가 사용 가능해지는 즉시 호출하는 것이 좋습니다.
  • 또는, 트랙 유형 = 로그인 (Track Type = Login ) 태그를 인증이 발생한 후 등 언제든지 트리거할 수 있습니다. 사용자 ID를 사용할 수 있게 되는 즉시 호출하는 것이 좋습니다. 참고: 이 태그를 호출해도 이벤트는 트리거되지 않습니다. 향후 발생할 모든 이벤트 트리거에 추가될 사용자 ID만 설정합니다!

Singular로 사용자 ID를 설정하려면 "로그인" 추적 유형의 Singular 태그를 추가하세요:

  1. Google 태그 관리자 계정에서 태그 > 새로 만들기를 클릭하세요.
  2. 태그 구성 창에서 태그 구성을 클릭하고 태그유형 메뉴에서 "Singular 웹 추적"을 선택합니다.
  3. 추적 유형에서 "로그인"을 선택합니다.
  4. 사용자 지정 ID 항목에 사용자 ID를 포함하는 Google 태그 관리자 변수를 입력합니다.
  5. 트리거링을 클릭하고 트리거링 이벤트를 추가합니다: 사용자 로그인 또는 등록.
  6. 저장을 클릭합니다.

image4.png

사용자 ID를 해제하려면 "로그아웃" 추적 유형의 태그를 추가하세요:

  1. Google 태그 관리자 계정에서 태그 > 새로 만들기를 클릭합니다.
  2. 태그 구성 창에서 태그 구성을 클릭하고 태그유형 메뉴에서 "Singular 웹 추적"을 선택합니다.
  3. 추적 유형에서 "로그아웃"을 선택합니다.
  4. 트리거링을 클릭하고 트리거링 이벤트를 추가합니다: 사용자 로그아웃.
  5. 저장을 클릭합니다.

image1.png

참고: 

  • 사용자 ID는 로그아웃 추적 유형을 사용하여 해제하거나 사용자가 로컬 스토리지를 삭제할 때까지 유지됩니다.
  • 웹사이트를 닫거나 새로 고침해도 사용자 ID는 유지됩니다.
  • 시크릿 모드와 같은 비공개 모드에서 브라우징하면 브라우저를 닫을 때 로컬 스토리지가 자동으로 삭제되므로 Singular가 사용자 ID를 지속적으로 유지하지 못합니다.

5단계: 이벤트 중복 제거(선택 사항)

Google 태그 관리자(GTM)의 이벤트 중복 제거

중요! GTM 트리거가 짧은 시간 내에 동일한 Singular 이벤트를 여러 번 발생시킬 수 있는 경우(예: 빠른 반복 클릭 또는 동일한 동작에 대한 여러 트리거), Singular의 선택적 이벤트 중복 제거 기능을 활성화하여 중복 내보내기를 자동으로 억제하십시오.

GTM에서 중복이 발생하는 이유

GTM은 동일한 사용자 행동에 대해 태그를 여러 번 평가 및 발동할 수 있습니다(예: 반복되는 트리거 조건 또는 여러 이벤트 리스너). 이로 인해 Singular 이벤트 내보내기가 중복될 수 있습니다.


GTM 중복 제거 방법 (권장)

Singular SDK 이벤트 중복 제거: Singular 웹 SDK GTM 템플릿에서 중복 제거를 활성화하여 구성 가능한 시간 창 내에서 동일한 중복 제거 매개변수와 일치하는 중복 이벤트를 제거합니다.


구현 단계

이벤트 중복 제거 활성화: Singular 웹 SDK 초기화 태그/템플릿에서 이벤트 중복 제거 옵션을 활성화합니다.

eventDeduplication.png

시간 창 설정(선택 사항): 두 이벤트를 중복으로 간주하는 최대 시간 (밀리초 단위)을 구성합니다(기본값: 1000ms / 1초).


중복 제거 작동 방식

활성화 시 SDK는 다음 매개변수 해시를 사용하여 동일한 이벤트가 시간 창 내에 재발생할 경우 이벤트(페이지 방문 제외)를 억제합니다: EventName, EventProductName, IsRevenueEvent, CustomUserId, GlobalProperties, MatchId, WebUrl(구매 및 사용자 정의 인자 등 이벤트 "추가" 페이로드 포함).


6단계: GTM 구현 테스트

  1. GTM 미리보기 모드를 열고 웹사이트를 로드합니다.
  2. 다음 사항을 확인하십시오:
  • SDK가 로드되는지 ( singular-sdk.js 로의 네트워크 요청)
  • 이벤트가 예상대로 트리거되며, 각 액션당 한 번만 발생함
  • singular 관련 브라우저 콘솔 오류 없음
  • 네트워크 요청이 sdk-api-v1.singular.net로 전송됨
  1. 브라우저 개발자 도구 네트워크 탭에서 올바른 페이로드가 전송되는지 확인하세요

성공! 네트워크 요청에서 올바른 Singular 이벤트가 표시되고 중복이 없다면 실행 준비 완료!


7단계: 웹-앱 전달 구현

웹-앱 어트리뷰션 전달

Singular 웹 SDK를 사용하여 웹사이트에서 모바일 앱으로의 사용자 여정을 추적하세요. 이를 통해 웹 캠페인에 대한 정확한 모바일 앱 설치 및 리인게이지먼트 어트리뷰션이 가능해집니다. 데스크톱 사용자를 위한 QR 코드 지원 포함, 웹-앱 전달 설정 방법은 다음 단계를 따르세요. 웹-앱 전달 설정 가이드를 참조하여 모바일 웹 어트리뷰션을 위한 Singular 웹 SDK를 구성하세요.

  1. 모바일 웹 어트리뷰션용 Singular 웹 SDK 구성을 위해 웹사이트-모바일 앱 어트리뷰션 전달 가이드 를따르세요.
  2. 모바일 웹-앱 추적 방법:
    • 앱 열기 태그를 추가하고 앱 열기 또는 설치 버튼 클릭 시 발동 트리거를 설정하세요.
      1. 앱 실행 태그 설정 시, Singular 링크 관리 페이지에서 모바일 웹-앱 기본 링크를 지정하세요.

        openApp.png

  3. 데스크톱 웹-앱 전환 추적:
    • QR 코드 생성기 정리 태그 생성
      1. GTM에서 태그 > 새로 만들기로 이동하여 사용자 정의 HTML을 선택하세요.
      2. 코드 추가 위치:
        • 선택한 QRCode 라이브러리 삽입
        • 다음에서 웹-투-앱 링크를 가져옵니다: window.singularSdk.buildWebToAppLink(baselink);
        • 반환된 링크로 QR 코드 생성
        • 페이지의 QR코드 이미지를 업데이트하세요.
      3. 태그 이름 지정: "Singular - QR 코드 생성기 (정리)"
      4. 트리거 불필요: 정리 태그는 메인 태그의 트리거를 상속합니다
      5. Singular 초기화 태그 구성:
        • 태그 구성클릭
        • 고급 설정 > 태그 순서로이동
        • "[Singular 초기화 태그] 실행 후 정리 태그 실행" 선택
        • 드롭다운에서 QR 코드 생성기 태그 선택
        • 태그를 저장하고 미리보기 모드에서 테스트하세요.

팁! 모바일 인앱 브라우저 웹 뷰(Facebook, Instagram, TikTok 등에서 사용되는 방식)는 사용자가 기기의 기본 브라우저로 이동할 경우 Singular 기기 ID가 변경되어 어트리뷰션에 차질을 빚을 수 있습니다.

이를 방지하려면 각 광고 네트워크에 대해 항상 적절한 Singular 추적 링크 형식을 사용하십시오:


고급 주제

Singular 배너

Singular 배너 활성화
#

INFO: Singular 배너는 엔터프라이즈 기능입니다. 이 기능에 대한 자세한 내용은 담당 고객 성공 매니저에게 문의하십시오.

Singular 배너는 모바일 웹사이트에 표시되어 웹 사용자를 원활하게 앱으로 유도하고 가장 관련성 높은 앱 콘텐츠를 표시할 수 있습니다. 웹사이트에서 Singular 배너를 활성화하면 조직은 Singular 배너 UI를 통해 배너를 쉽게 디자인, 배포 및 유지 관리할 수 있습니다.

관련 문서

단계별 구현 가이드

  1. 웹사이트에 Singular 웹SDK GTM 초기화 태그 추가

    배너 구현을 진행하기 전에 위의 연동 가이드를따라 웹사이트에 Singular GTM WebSDK를 추가하세요.

  2. 초기화 태그 구성에서 스마트 배너 및 웹-투-앱 지원을 활성화하세요. 고급 설정 태그 발동 우선순위를 99로 설정하는 것이 좋습니다. 이는 동일한 발동 트리거를 가진 다른 태그보다 초기화를 우선시합니다.

    gtm-banners.png

  3. 배너 표시 태그 추가

    페이지 로드 후 스마트 배너를 표시하려면 모든 페이지 뷰에서 발동되는 배너 표시 태그를 추가하세요. 고급 설정에서 태그 발동 우선순위를 1로 설정하는 것이 좋습니다. 이렇게 하면 이 태그가 마지막에 발동되도록 지연됩니다.

    showBanner.png

    특정 페이지에서 배너를 숨기려면 배너 숨기기 태그를 추가하고 필요에 따라 트리거를 설정하세요.

  4. [고급 옵션] 링크 설정 사용자 지정

    Singular는 코드를 통해 배너 내 링크를 개인화하는 방법을 제공합니다. 링크를 맞춤 설정하려면:

    링크 맞춤 설정 방법:

    • '배너 표시' 태그 설정에서 '링크 매개변수(선택 사항)' 섹션을 확장하세요. 배너 클릭 동작의 리디렉션 로직에 대한 특정 재정의 값을 추가할 수 있습니다.

      bannerRedirects.png

    옵션 목록:

    메서드 설명
    Android Redirect URL Android 앱 다운로드 페이지(일반적으로 Play 스토어 페이지)로 리디렉션 링크 전달.
    Android Deep Link Android 앱 내부의 특정 페이지로 연결되는 딥 링크를 전달합니다.
    Android Deferred Deep Link 지연 딥 링크 전달, 즉 사용자가 아직 설치하지 않은 Android 앱 내 페이지로의 링크. (예: 사용자가 아직 설치하지 않은 앱 내 특정 페이지로의 링크)
    iOS Redirect URL iOS 앱 다운로드 페이지(일반적으로 App Store 페이지)로 리디렉션 링크를 전달합니다.
    iOS Deep Link iOS 앱 내 페이지로의 딥 링크를 전달합니다.
    iOS Deferred Deep Link 사용자가 아직 설치하지 않은 iOS 앱 내 페이지로의 링크인 지연 딥 링크를 전달합니다.

글로벌 속성 추가

글로벌 속성
#

Singular SDK를 사용하면 모든 세션 및 이벤트와 함께 Singular 서버로 전송될 사용자 정의 속성을 정의할 수 있습니다. 이러한 속성은 사용자 정보, 앱 모드/상태 또는 선택한 기타 정보를 나타낼 수 있습니다.

  • 유효한 JSON 객체로 최대 5개의 글로벌 속성을 정의할 수 있습니다. 글로벌 속성은 브라우저의 localStorage 에 지워질 때까지 저장됩니다.

  • 각 속성 이름과 값은 최대 200자까지 가능합니다. 이보다 긴 속성 이름이나 값을 전달하면 200자로 잘립니다.

  • 글로벌 속성은 Singular의 사용자 수준 이벤트 로그와 포스트백에 반영됩니다.

이제 Google Tag Manager 추적 템플릿 초기화 시 글로벌 속성이 지원됩니다. Singular GTM 초기화 태그 유형에서 속성 JSON 객체를 설정하고 기존 속성 덮어쓰기 여부를 선택하세요.

GTM의 글로벌 속성 태그 유형

초기화 시 글로벌 속성을 설정하려면 초기화 태그 유형을 사용하십시오. 초기화 후 런타임 업데이트에는 전용 글로벌 속성 태그 유형(설정, 가져오기, 해제, 지우기)을 사용하십시오.

Set on InitializationSet Global PropertiesGet Global PropertiesUnset Global PropertyClear Global Properties

초기화 태그 구성

Singular GTM 초기화 태그 유형에서 Global Properties (객체)를 설정하려면 KeyValue 필드에 변수, 데이터 레이어 값 또는 텍스트를 할당하세요. 필요에 따라 Override (true/false) 필드를 조정하세요. Overridefalse 인 경우 기존 속성은 변경되지 않으며, true 인 경우 기존 속성이 대체됩니다.

gpgtm.png


자연 검색 추적

자연 검색 추적 예시
#

자연 검색 추적 설정 태그 생성

중요! - 이 태그는 Singular SDK 초기화 전에 반드시 실행되어야 합니다!

이 커스텀 HTML 태그는 문서 URL을 수정하여 Singular 웹 SDK가 초기화 중에 읽어야 하는 자연 검색 추적 매개변수(wpsrc 및 wpcn)를 추가합니다. 태그 순서는 올바른 실행 순서를 보장하기 위해 매우 중요합니다.

이 예시는 워크어라운드 솔루션으로 제공되어 유기적 검색 추적을 활성화합니다. 코드는 예시 용도로만 사용해야 하며, 마케팅 부서의 요구에 따라 웹 개발자가 업데이트 및 유지 관리해야 합니다. 유기적 검색 추적은 광고주마다 다른 의미를 가질 수 있습니다. 샘플을 검토하고 필요에 맞게 조정하십시오.

사용 이유

  • 캠페인 매개변수가 없더라도 자연 검색 방문이 제대로 추적되도록 보장합니다.

  • 명확한 기여도 측정을 위해 URL에 (리퍼러)의 값을 가진 Singular형 "Source" 매개변수 wpsrc및 "Campaign Name" 매개변수 wpcn 을 "OrganicSearch"로 추가합니다.

  • 나중에 사용할 수 있도록 현재 URL과 리퍼러를 localStorage에 저장합니다.

  • 순수 자바Script, 제로 의존성, 쉬운 연동.

작동 방식

  1. 페이지 URL에서 알려진 캠페인 매개변수(Google, Facebook, TikTok, UTM 등)를 확인합니다.

  2. 캠페인 매개변수가 없고 리퍼러가 검색 엔진인 경우 다음을 추가합니다:

    • wpsrc (리퍼러를 값으로 함)
    • wpcn (값으로 'OrganicSearch' 사용)
  3. 페이지를 재로드하지 않고 브라우저의 URL을 업데이트합니다.

  4. 현재 URL과 리퍼러를 localStoragesng_urlsng_ref 으로 저장합니다.

사용법

  1. GTM에서 태그 > 새로 만들기로 이동하여 태그 구성을 클릭한 후 사용자 정의 HTML을 선택합니다.
  2. 아래와 같이 전체 JavaScript 코드를 붙여넣습니다.
  3. 이 태그에 대한 트리거 생성은 생략합니다. 이 태그는 Singular 초기화 태그보다 먼저 실행되어야 하므로, 태그 시퀀싱을 사용하여 초기화 태그보다 먼저 발동되도록 구성합니다.

    1. "Singular - Organic Search Setup"과 같이 설명적인 이름을 사용하세요.
    2. Singular 웹 SDK 초기화 태그를 엽니다.
    3. 태그 구성을 클릭하세요.
    4. 고급 설정 > 태그 시퀀싱으로 이동합니다.
    5. "[Singular 초기화 태그] 실행 전에 설정 태그 실행"을 선택하세요.
    6. 드롭다운에서 "Singular - Organic Search Setup" 태그를 선택하세요. 권장: [설정 태그]가 실패할 경우 [Singular Init Tag]를 실행하지 않도록 "[설정 태그]가 실패할 경우 [Singular Init Tag] 실행 안 함"을 선택하세요.
    7. 권장: URL 수정이 불완전한 상태에서 초기화가 이루어지지 않도록 [설정 태그] 실패 시 [Singular 초기화 태그]를 발동하지 않도록 체크하세요.
    8. organic_search.png
    9. 태그 저장.
  4. GTM의 미리보기 모드로 확인하세요:

    • 설정 태그가 먼저 실행되어 URL을 수정합니다.
    • Singular 초기화 태그가 두 번째로 실행되어 수정된 URL 매개변수를 읽습니다.
    • 태그 간 타이밍 충돌이 발생하지 않습니다.

고급 태그 시퀀싱의 경우: Google의 공식 문서를 참조하세요:


Organic Search Tracking Code
<script>
(function() {
    // singular-web-organic-search-tracking: setupOrganicSearchTracking.js
    // Tracks organic search referrals by appending wpsrc and wpcn to the URL if no campaign parameters exist and the referrer is a search engine.

    // Configuration for debugging (set to true to enable logs)
    var debug = true;

    // List of campaign parameters to check for exclusion
    var campaignParams = [
        'gclid', 'fbclid', 'ttclid', 'msclkid', 'twclid', 'li_fat_id',
        'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'wpsrc'
    ];

    // Whitelist of legitimate search engine domains (prevents false positives)
    var legitimateSearchEngines = new Set([
        // Google domains
        'google.com', 'google.co.uk', 'google.ca', 'google.com.au', 'google.de', 
        'google.fr', 'google.it', 'google.es', 'google.co.jp', 'google.co.kr',
        'google.com.br', 'google.com.mx', 'google.co.in', 'google.ru', 'google.com.sg',
        
        // Bing domains  
        'bing.com', 'bing.co.uk', 'bing.ca', 'bing.com.au', 'bing.de',
        
        // Yahoo domains
        'yahoo.com', 'yahoo.co.uk', 'yahoo.ca', 'yahoo.com.au', 'yahoo.de',
        'yahoo.fr', 'yahoo.it', 'yahoo.es', 'yahoo.co.jp',
        
        // Other search engines
        'baidu.com', 'duckduckgo.com', 'yandex.com', 'yandex.ru',
        'ask.com', 'aol.com', 'ecosia.org', 'startpage.com', 
        'qwant.com', 'seznam.cz', 'naver.com', 'daum.net'
    ]);

    // Extract main domain from hostname (removes subdomains)
    function getMainDomain(hostname) {
        if (!hostname) return '';
        
        var lowerHost = hostname.toLowerCase();
        
        // Handle special cases for known search engines with country codes
        var searchEnginePatterns = {
            'google': function(host) {
                // Match google.TLD patterns more precisely
                if (host.indexOf('google.co.') !== -1 || host.indexOf('google.com') !== -1) {
                    var parts = host.split('.');
                    for (var i = 0; i < parts.length - 1; i++) {
                        if (parts[i] === 'google') {
                            return parts.slice(i).join('.');
                        }
                    }
                }
                return null;
            },
            'bing': function(host) {
                if (host.indexOf('bing.co') !== -1 || host.indexOf('bing.com') !== -1) {
                    var parts = host.split('.');
                    for (var i = 0; i < parts.length - 1; i++) {
                        if (parts[i] === 'bing') {
                            return parts.slice(i).join('.');
                        }
                    }
                }
                return null;
            },
            'yahoo': function(host) {
                if (host.indexOf('yahoo.co') !== -1 || host.indexOf('yahoo.com') !== -1) {
                    var parts = host.split('.');
                    for (var i = 0; i < parts.length - 1; i++) {
                        if (parts[i] === 'yahoo') {
                            return parts.slice(i).join('.');
                        }
                    }
                }
                return null;
            }
        };
        
        // Try specific patterns for major search engines
        for (var engine in searchEnginePatterns) {
            if (lowerHost.indexOf(engine) !== -1) {
                var result = searchEnginePatterns[engine](lowerHost);
                if (result) return result;
            }
        }
        
        // Handle other known engines with simple mapping
        var otherEngines = {
            'baidu.com': 'baidu.com',
            'duckduckgo.com': 'duckduckgo.com', 
            'yandex.ru': 'yandex.ru',
            'yandex.com': 'yandex.com',
            'ask.com': 'ask.com',
            'aol.com': 'aol.com',
            'ecosia.org': 'ecosia.org',
            'startpage.com': 'startpage.com',
            'qwant.com': 'qwant.com',
            'seznam.cz': 'seznam.cz',
            'naver.com': 'naver.com',
            'daum.net': 'daum.net'
        };
        
        for (var domain in otherEngines) {
            if (lowerHost.indexOf(domain) !== -1) {
                return otherEngines[domain];
            }
        }
        
        // Fallback: Extract main domain by taking last 2 parts (for unknown domains)
        var parts = hostname.split('.');
        if (parts.length >= 2) {
            return parts[parts.length - 2] + '.' + parts[parts.length - 1];
        }
        
        return hostname;
    }

    // Get query parameter by name, using URL.searchParams with regex fallback for IE11
    function getParameterByName(name, url) {
        if (!url) url = window.location.href;
        try {
            return new URL(url).searchParams.get(name) || null;
        } catch (e) {
            if (debug) console.warn('URL API not supported, falling back to regex:', e);
            name = name.replace(/[\[\]]/g, '\\$&');
            var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)');
            var results = regex.exec(url);
            if (!results) return null;
            if (!results[2]) return '';
            return decodeURIComponent(results[2].replace(/\+/g, ' '));
        }
    }

    // Check if any campaign parameters exist in the URL
    function hasAnyParameter(url, params) {
        for (var i = 0; i < params.length; i++) {
            if (getParameterByName(params[i], url) !== null) {
                return true;
            }
        }
        return false;
    }

    // Improved search engine detection - only checks hostname, uses whitelist
    function isSearchEngineReferrer(referrer) {
        if (!referrer) return false;
        
        var hostname = '';
        try {
            hostname = new URL(referrer).hostname.toLowerCase();
        } catch (e) {
            // Fallback regex for hostname extraction (IE11 compatibility)
            var match = referrer.match(/^(?:https?:\/\/)?([^\/\?#]+)/i);
            hostname = match ? match[1].toLowerCase() : '';
        }
        
        if (!hostname) return false;
        
        // First check: exact match against whitelist
        if (legitimateSearchEngines.has(hostname)) {
            if (debug) console.log('Exact match found for:', hostname);
            return true;
        }
        
        // Second check: subdomain of legitimate search engine
        var hostParts = hostname.split('.');
        if (hostParts.length >= 3) {
            // Try domain.tld combination (e.g., google.com from www.google.com)
            var mainDomain = hostParts[hostParts.length - 2] + '.' + hostParts[hostParts.length - 1];
            if (legitimateSearchEngines.has(mainDomain)) {
                if (debug) console.log('Subdomain match found for:', hostname, '-> main domain:', mainDomain);
                return true;
            }
            
            // Try last 3 parts for country codes (e.g., google.co.uk from www.google.co.uk)
            if (hostParts.length >= 3) {
                var ccDomain = hostParts[hostParts.length - 3] + '.' + hostParts[hostParts.length - 2] + '.' + hostParts[hostParts.length - 1];
                if (legitimateSearchEngines.has(ccDomain)) {
                    if (debug) console.log('Country code domain match found for:', hostname, '-> cc domain:', ccDomain);
                    return true;
                }
            }
        }
        
        if (debug) {
            console.log('Hostname not recognized as legitimate search engine:', hostname);
        }
        
        return false;
    }

    // Main function to update URL with organic search tracking parameters
    function setupOrganicSearchTracking() {
        var url = window.location.href;
        var referrer = document.referrer || '';

        // Store URL and referrer in localStorage
        try {
            localStorage.setItem('sng_url', url);
            localStorage.setItem('sng_ref', referrer);
        } catch (e) {
            if (debug) console.warn('localStorage not available:', e);
        }

        if (debug) {
            console.log('Current URL:', url);
            console.log('Referrer:', referrer);
        }

        // Skip if campaign parameters exist or referrer is not a search engine
        var hasCampaignParams = hasAnyParameter(url, campaignParams);
        if (hasCampaignParams || !isSearchEngineReferrer(referrer)) {
            if (debug) console.log('Skipping URL update: Campaign params exist or referrer is not a legitimate search engine');
            return;
        }

        // Extract and validate referrer hostname
        var referrerHostname = '';
        try {
            referrerHostname = new URL(referrer).hostname;
        } catch (e) {
            if (debug) console.warn('Invalid referrer URL, falling back to regex:', e);
            var match = referrer.match(/^(?:https?:\/\/)?([^\/]+)/i);
            referrerHostname = match ? match[1] : '';
        }

        // Extract main domain from hostname
        var mainDomain = getMainDomain(referrerHostname);
        
        if (debug) {
            console.log('Full hostname:', referrerHostname);
            console.log('Main domain:', mainDomain);
        }

        // Only proceed if main domain is valid and contains safe characters
        if (!mainDomain || !/^[a-zA-Z0-9.-]+$/.test(mainDomain)) {
            if (debug) console.log('Skipping URL update: Invalid or unsafe main domain');
            return;
        }

        // Update URL with wpsrc and wpcn parameters
        var urlObj;
        try {
            urlObj = new URL(url);
        } catch (e) {
            if (debug) console.warn('URL API not supported, cannot modify URL:', e);
            return;
        }
        
        // Set wpsrc to the main domain (e.g., google.com instead of tagassistant.google.com)
        urlObj.searchParams.set('wpsrc', mainDomain);
        
        // Set wpcn to 'Organic Search' to identify the campaign type
        urlObj.searchParams.set('wpcn', 'Organic Search');

        // Update the URL without reloading (check if history API is available)
        if (window.history && window.history.replaceState) {
            try {
                window.history.replaceState({}, '', urlObj.toString());
                if (debug) console.log('Updated URL with organic search tracking:', urlObj.toString());
            } catch (e) {
                if (debug) console.warn('Failed to update URL:', e);
            }
        } else {
            if (debug) console.warn('History API not supported, cannot update URL');
        }
    }

    // Execute the function
    setupOrganicSearchTracking();
})();
</script>

크로스 서브도메인 추적

기본적으로 Singular 웹사이트 SDK는 Singular 기기 ID를 생성하고 브라우저 스토리지를 사용하여 이를 유지합니다. 이 스토리지는 서브도메인 간에 공유될 수 없으므로, SDK는 각 서브도메인마다 새 ID를 생성하게 됩니다.

서브도메인 간에 Singular 기기 ID를 지속 유지하려면 다음 옵션 중 하나를 사용할 수 있습니다: 방법 A: 쿠키를 사용한 자동 지속 유지

방법 B (고급): Singular 디바이스 ID 수동 설정
#

방법 B (고급): Singular 기기 ID 수동 설정

Singular SDK가 기기 ID를 자동으로 지속하지 않도록 하려면, 도메인 간에 ID를 수동으로 지속할 수 있습니다. 예를 들어 최상위 도메인 쿠키나 서버 측 쿠키를 사용하세요. 값은 Singular에서 생성한 유효한 uuid4 형식의 ID여야 합니다.

참고: Singular Device ID는 사용자 정의 JavaScript 변수를 정의하고 Init 추적 유형 태그 호출 후 singularSdk.getSingularDeviceId()를 호출하여 읽을 수 있습니다.

mceclip2.png


일반적인 GTM 구현 문제점

필수 기기 식별자
이벤트가 여러 번 발생함 트리거를 세분화하거나 제한하고, "페이지당 한 번"을 사용하거나 조건을 추가하세요
잘못된 제품 ID 형식 제품 ID는 역 DNS 표기법(com.company.site)을 사용해야 함
이벤트가 추적되지 않음 이벤트 이름의 철자와 대소문자를 확인하십시오. 이벤트 태그가 발생하기 전에 singular SDK가 로드되었는지 확인하십시오
SDK Script 차단됨 광고 차단기 또는 제한적인 콘텐츠 보안 정책; 문제가 지속될 경우 네이티브 JS 구현으로 전환을 고려하십시오

모범 사례

  • GTM 체계 유지: 태그와 트리거를 명확하게 명명하고 목적을 문서화하십시오.
  • 사용되지 않거나 구식 태그를 정기적으로 점검하십시오.
  • 중복 이벤트 발생 위험을 줄이기 위해 트리거 수를 최소화하십시오.
  • 변경 후에는 항상 GTM의 미리보기 모드에서 철저히 테스트한 후 실제 적용하십시오.
  • 쿠키 기반 추적(크로스 서브도메인 추적)을 사용하는 경우 개인정보 처리방침을 해당 내용에 맞게 업데이트하십시오.

관련 문서