feat: add GTM editor & validator skills (61, 62), update GTM audit (60)
Restructure GTM skills into audit → edit → validate workflow: - 60-gtm-audit: MCP-based page scan, site audit, gap analysis, tag design (new code/SKILL.md) - 61-gtm-editor: tag/trigger/variable creation via GTM API, ES5 Custom HTML, dataLayer generation - 62-gtm-validator: QA toolkit with 7 validation modes, naming conventions, cross-platform mapping All three skills use DTM Agent + Chrome DevTools MCP with clear handoff patterns. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
16
AGENTS.md
16
AGENTS.md
@@ -129,9 +129,19 @@ Task 2: general-purpose - "Implement the planned skill" # Needs Task 1 result
|
|||||||
|
|
||||||
### GTM Skills (60-69)
|
### GTM Skills (60-69)
|
||||||
|
|
||||||
- Use **gtm-manager** agent for GTM-specific debugging
|
Three specialized skills with clear handoff workflow: **audit → edit → validate**
|
||||||
- Requires Chrome GTM Debug profile for live testing
|
|
||||||
- Scripts interact with GTM API and dataLayer
|
| Skill | Purpose | MCP Tools |
|
||||||
|
|-------|---------|-----------|
|
||||||
|
| 60-gtm-audit | Scan pages, gap analysis, tag design blueprints | DTM Agent + Chrome DevTools |
|
||||||
|
| 61-gtm-editor | Create/modify tags, triggers, variables via GTM API | DTM Agent + Chrome DevTools |
|
||||||
|
| 62-gtm-validator | QA tag firing, trigger selectors, naming conventions | DTM Agent + Chrome DevTools |
|
||||||
|
|
||||||
|
- **gtm-audit** Mode D outputs blueprints → hand off to **gtm-editor** for implementation
|
||||||
|
- **gtm-editor** creates tags → hand off to **gtm-validator** for QA verification
|
||||||
|
- **gtm-validator** finds broken selectors → hand back to **gtm-editor** for fixes
|
||||||
|
- All three use DTM Agent MCP for container operations and Chrome DevTools MCP for browser inspection
|
||||||
|
- Audit reports write to Notion GTM Audit Log database
|
||||||
|
|
||||||
### Jamie Clinic Skills (40-49)
|
### Jamie Clinic Skills (40-49)
|
||||||
|
|
||||||
|
|||||||
@@ -68,9 +68,9 @@ This is a Claude Skills collection repository containing:
|
|||||||
|
|
||||||
| # | Skill | Purpose | Trigger |
|
| # | Skill | Purpose | Trigger |
|
||||||
|---|-------|---------|---------|
|
|---|-------|---------|---------|
|
||||||
| 60 | gtm-audit | GTM container audit | "audit GTM", "GTM analysis" |
|
| 60 | gtm-audit | GTM audit — page scan, site audit, gap analysis, tag design | "GTM audit", "scan page", "tracking coverage", "gap analysis" |
|
||||||
| 61 | gtm-manager | GTM management + dataLayer | "GTM manager", "dataLayer" |
|
| 61 | gtm-editor | GTM tag/trigger/variable creation via API, ES5 Custom HTML, dataLayer | "create GTM tag", "generate dataLayer", "modify trigger", "manage GTM" |
|
||||||
| 62 | gtm-guardian | Live tag monitoring & validation | "monitor GTM", "tag validation" |
|
| 62 | gtm-validator | GTM QA — tag firing, trigger testing, naming conventions, cross-platform | "validate tags", "QA GTM", "debug GTM", "naming conventions" |
|
||||||
|
|
||||||
### Notion Tools (31-39)
|
### Notion Tools (31-39)
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
> **Internal R&D Repository** - This repository is restricted for internal use only.
|
> **Internal R&D Repository** - This repository is restricted for internal use only.
|
||||||
|
|
||||||
A collection of **62 custom Claude Skills** for OurDigital workflows, D.intelligence Agent Corps (9-agent business operations suite), Jamie Plastic Surgery Clinic brand management, SEO/GTM tools, NotebookLM automation, Notion integrations, reference documentation curation, macOS system optimization, and multi-agent collaboration.
|
A collection of **64 custom Claude Skills** for OurDigital workflows, D.intelligence Agent Corps (9-agent business operations suite), Jamie Plastic Surgery Clinic brand management, SEO/GTM tools, NotebookLM automation, Notion integrations, reference documentation curation, macOS system optimization, and multi-agent collaboration.
|
||||||
|
|
||||||
## Quick Install
|
## Quick Install
|
||||||
|
|
||||||
@@ -88,9 +88,9 @@ cd our-claude-skills/custom-skills/_ourdigital-shared
|
|||||||
|
|
||||||
| # | Skill | Purpose |
|
| # | Skill | Purpose |
|
||||||
|---|-------|---------|
|
|---|-------|---------|
|
||||||
| 60 | `gtm-audit` | GTM container audit with Chrome DevTools |
|
| 60 | `gtm-audit` | GTM container audit — page scan, site audit, gap analysis, tag design (DTM Agent + Chrome DevTools MCP) |
|
||||||
| 61 | `gtm-manager` | GTM management + dataLayer generation |
|
| 61 | `gtm-editor` | GTM tag/trigger/variable creation via API, ES5 Custom HTML, dataLayer generation, workspace lifecycle |
|
||||||
| 62 | `gtm-guardian` | Live tag monitoring & validation |
|
| 62 | `gtm-validator` | GTM QA — tag firing verification, trigger testing, dataLayer schema, naming conventions, cross-platform mapping |
|
||||||
|
|
||||||
### Notion Tools (31-39)
|
### Notion Tools (31-39)
|
||||||
|
|
||||||
|
|||||||
140
custom-skills/60-gtm-audit/code/SKILL.md
Normal file
140
custom-skills/60-gtm-audit/code/SKILL.md
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
---
|
||||||
|
name: gtm-audit
|
||||||
|
description: >
|
||||||
|
Automated GTM audit and analysis. Scans websites for tracking coverage, compares
|
||||||
|
against GTM container config, identifies gaps, and reports to Notion. Uses Chrome
|
||||||
|
DevTools MCP for browser inspection and DTM Agent MCP for container operations.
|
||||||
|
Triggers on "GTM audit", "audit website", "scan page", "check tracking",
|
||||||
|
"tracking coverage", "gap analysis", "site audit", "tag firing check".
|
||||||
|
NOT for creating/modifying tags (use gtm-editor) or validating/QA (use gtm-validator).
|
||||||
|
---
|
||||||
|
|
||||||
|
# GTM Audit Skill
|
||||||
|
|
||||||
|
Analyze GTM implementations by scanning websites and comparing against container configuration.
|
||||||
|
|
||||||
|
## Available Tools
|
||||||
|
|
||||||
|
### Browser Inspection (Chrome DevTools MCP)
|
||||||
|
- `navigate_page` — Load target URL
|
||||||
|
- `evaluate_script` — Capture dataLayer, detect GTM containers, check JS globals
|
||||||
|
- `list_network_requests` — Capture tag firing network requests
|
||||||
|
- `take_screenshot` — Document page state
|
||||||
|
- `click` / `hover` — Simulate user interactions
|
||||||
|
- `list_console_messages` — Check for JS errors
|
||||||
|
|
||||||
|
### GTM Container (DTM Agent MCP)
|
||||||
|
- `dtm_status` — Check auth and active container
|
||||||
|
- `dtm_list_tags` / `dtm_list_triggers` / `dtm_list_variables` — Get container config
|
||||||
|
- `dtm_audit_scan` — Scan page with collected data (returns structured PageData)
|
||||||
|
- `dtm_audit_gap` — Run gap analysis (returns GapReport with OK/MISSING/ORPHAN)
|
||||||
|
|
||||||
|
### Reporting (Notion MCP)
|
||||||
|
- Write findings to GTM Audit Log: `collection://2cf581e5-8a1e-815a-a7f8-000b88b93585`
|
||||||
|
|
||||||
|
## 4 Audit Modes
|
||||||
|
|
||||||
|
### Mode A: Page Scan (`/gtm-audit scan <url>`)
|
||||||
|
|
||||||
|
Quick single-page diagnostics. No GTM container needed.
|
||||||
|
|
||||||
|
1. `navigate_page` to URL, wait for load
|
||||||
|
2. `evaluate_script`: capture dataLayer
|
||||||
|
```javascript
|
||||||
|
JSON.stringify(window.dataLayer || [])
|
||||||
|
```
|
||||||
|
3. `evaluate_script`: detect GTM containers
|
||||||
|
```javascript
|
||||||
|
(function(){var s=document.querySelectorAll('script[src*="googletagmanager.com/gtm.js"]');var ids=[];s.forEach(function(el){var m=el.src.match(/id=(GTM-[A-Z0-9]+)/);if(m)ids.push(m[1]);});return JSON.stringify(ids);})()
|
||||||
|
```
|
||||||
|
4. `evaluate_script`: detect tracking pixels
|
||||||
|
```javascript
|
||||||
|
(function(){var f=[];if(window.fbq)f.push('Meta Pixel');if(window.gtag)f.push('GA4');if(window.clarity)f.push('Clarity');if(window.kakaoPixel)f.push('Kakao');if(window.wcs)f.push('Naver');if(window.ttq)f.push('TikTok');return JSON.stringify(f);})()
|
||||||
|
```
|
||||||
|
5. `list_network_requests` — filter for tag domains
|
||||||
|
6. Call `dtm_audit_scan` with collected html, network_requests, datalayer_json
|
||||||
|
7. Report findings
|
||||||
|
|
||||||
|
### Mode B: Site Audit (`/gtm-audit site <url>`)
|
||||||
|
|
||||||
|
Full website coverage audit with auto-discovery.
|
||||||
|
|
||||||
|
1. Navigate to root URL
|
||||||
|
2. `evaluate_script`: extract all internal links
|
||||||
|
3. Classify pages by type (home, product, service, form, blog, about)
|
||||||
|
4. For each page type: run Mode A scan
|
||||||
|
5. Build coverage matrix (page type × event × platform)
|
||||||
|
6. Run Mode C gap analysis against container
|
||||||
|
7. Write comprehensive report to Notion
|
||||||
|
|
||||||
|
### Mode C: Gap Analysis (`/gtm-audit gap <url>`) — MOST IMPORTANT
|
||||||
|
|
||||||
|
Compare GTM container config against actual tag firing.
|
||||||
|
|
||||||
|
1. `dtm_status` — verify auth and active container
|
||||||
|
2. `dtm_list_tags` + `dtm_list_triggers` — get expected configuration
|
||||||
|
3. `navigate_page` to URL
|
||||||
|
4. `evaluate_script` — capture dataLayer
|
||||||
|
5. `list_network_requests` — capture actual tag firings
|
||||||
|
6. Call `dtm_audit_gap` with network_requests
|
||||||
|
7. Analyze gaps: MISSING (in GTM, not firing), ORPHAN (firing, not in GTM)
|
||||||
|
8. Write to Notion GTM Audit Log
|
||||||
|
|
||||||
|
### Mode D: Tag Design (`/gtm-audit design <url>`)
|
||||||
|
|
||||||
|
AI-powered analysis of what SHOULD be tracked on a page.
|
||||||
|
|
||||||
|
1. `navigate_page` to URL
|
||||||
|
2. `take_screenshot` — visual reference
|
||||||
|
3. `evaluate_script` — inspect DOM: forms, buttons, links, product elements
|
||||||
|
4. `evaluate_script` — capture existing dataLayer schema
|
||||||
|
5. `dtm_list_tags` — check existing configuration
|
||||||
|
6. Design recommendations: which events, triggers, variables are needed
|
||||||
|
7. Output as structured blueprint (hand off to gtm-editor for implementation)
|
||||||
|
|
||||||
|
## Detection Patterns
|
||||||
|
|
||||||
|
### Cloudflare Zaraz Proxy
|
||||||
|
URLs with `/1tbv/` path proxy GA4/Ads through the site's domain:
|
||||||
|
```
|
||||||
|
https://site.com/1tbv/ag/g/c?tid=G-XXXXX → GA4
|
||||||
|
https://site.com/1tbv/ag/g/c?tid=AW-XXXXX → Google Ads
|
||||||
|
```
|
||||||
|
|
||||||
|
### Server-Side GTM (sGTM)
|
||||||
|
Custom subdomains: `stms.*`, `sgtm.*`, `ss.*`, `tag.*`
|
||||||
|
```
|
||||||
|
https://stms.site.com/g/collect?tid=G-XXXXX → GA4 via sGTM
|
||||||
|
```
|
||||||
|
|
||||||
|
### GA4 Multiple Endpoints
|
||||||
|
One GA4 page_view fires to multiple endpoints — this is normal:
|
||||||
|
- `google-analytics.com/g/collect`
|
||||||
|
- `analytics.google.com/g/s/collect`
|
||||||
|
- `stats.g.doubleclick.net/g/collect`
|
||||||
|
- `www.google.com/ccm/collect` (Consent Mode)
|
||||||
|
- Zaraz proxy endpoints
|
||||||
|
|
||||||
|
## Notion Report Fields
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|-------|-------|
|
||||||
|
| Site | domain name |
|
||||||
|
| URL | full URL |
|
||||||
|
| Container IDs | GTM-XXXXX |
|
||||||
|
| Audit Status | Pass / Warning / Fail |
|
||||||
|
| GTM Status | Installed / Not Found / Multiple Containers |
|
||||||
|
| Tags Fired | [GA4, Google Ads, Meta Pixel, Kakao, Naver, ...] |
|
||||||
|
| Journey Type | full / pageview / scroll / form / checkout / datalayer |
|
||||||
|
| Critical Issues | count |
|
||||||
|
| Issues Count | count |
|
||||||
|
| Summary | AI-generated findings |
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
- Always check `dtm_status` before gap analysis
|
||||||
|
- Wait for page to fully load before capturing network requests
|
||||||
|
- Use `evaluate_script` to check consent mode before interpreting missing tags
|
||||||
|
- Orphan count may be high due to GA4 multi-endpoint — note this in reports
|
||||||
|
- Hand off tag creation work to `gtm-editor` skill
|
||||||
|
- Hand off QA/validation work to `gtm-validator` skill
|
||||||
186
custom-skills/61-gtm-editor/code/SKILL.md
Normal file
186
custom-skills/61-gtm-editor/code/SKILL.md
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
---
|
||||||
|
name: gtm-editor
|
||||||
|
description: >
|
||||||
|
GTM implementation toolkit. Creates, updates, and modifies GTM tags, triggers,
|
||||||
|
variables via API. Generates Custom HTML with ES5 compliance. Handles workspace
|
||||||
|
lifecycle, DOM analysis for trigger design, and dataLayer code generation.
|
||||||
|
Triggers on "create GTM tag", "generate dataLayer", "modify trigger", "update variable",
|
||||||
|
"inject tracking code", "write custom HTML", "manage GTM", "design tags",
|
||||||
|
"create conversion tag", "set up tracking".
|
||||||
|
NOT for auditing (use gtm-audit) or validation/QA (use gtm-validator).
|
||||||
|
---
|
||||||
|
|
||||||
|
# GTM Editor Skill
|
||||||
|
|
||||||
|
Create, modify, and deploy GTM configurations via API. Generates ES5-compliant Custom HTML tags.
|
||||||
|
|
||||||
|
## Available Tools
|
||||||
|
|
||||||
|
### GTM Container Management (DTM Agent MCP)
|
||||||
|
- `dtm_status` — Check auth and active account/container
|
||||||
|
- `dtm_set_account` / `dtm_set_container` — Switch context
|
||||||
|
- `dtm_list_tags` / `dtm_get_tag` / `dtm_create_tag` / `dtm_update_tag` / `dtm_delete_tag`
|
||||||
|
- `dtm_list_triggers` / `dtm_get_trigger` / `dtm_create_trigger` / `dtm_delete_trigger`
|
||||||
|
- `dtm_list_variables` / `dtm_get_variable` / `dtm_create_variable` / `dtm_delete_variable`
|
||||||
|
- `dtm_list_folders` / `dtm_list_workspaces` / `dtm_get_workspace_status`
|
||||||
|
- `dtm_list_versions` / `dtm_get_live_version` / `dtm_create_version`
|
||||||
|
|
||||||
|
### Browser Analysis (Chrome DevTools MCP)
|
||||||
|
- `navigate_page` — Load page for DOM analysis
|
||||||
|
- `evaluate_script` — Inspect forms, buttons, links, CSS selectors
|
||||||
|
- `take_snapshot` — Get page structure for trigger design
|
||||||
|
- `list_network_requests` — Verify tags fire after changes
|
||||||
|
|
||||||
|
### Reporting (Notion MCP)
|
||||||
|
- Write implementation plans and tag configurations to Notion
|
||||||
|
|
||||||
|
## Core Capabilities
|
||||||
|
|
||||||
|
### 1. Tag Creation via GTM API
|
||||||
|
|
||||||
|
Create tags directly in GTM — no manual pasting. The API handles workspace lifecycle automatically (auto-creates new workspace if current one is published).
|
||||||
|
|
||||||
|
**GA4 Event Tags:**
|
||||||
|
```
|
||||||
|
GA4 event tags use type "gaawe" with:
|
||||||
|
- eventName: the GA4 event name (snake_case)
|
||||||
|
- measurementIdOverride: {{GA4 Measurement ID variable}} (NOT measurementId)
|
||||||
|
- eventParameters: list of name/value maps
|
||||||
|
```
|
||||||
|
|
||||||
|
**CRITICAL: measurementIdOverride, NOT measurementId**
|
||||||
|
GA4 event tags inherit from config — use `measurementIdOverride` to reference the measurement ID variable. The API rejects `measurementId`.
|
||||||
|
|
||||||
|
### 2. Trigger Design from DOM Analysis
|
||||||
|
|
||||||
|
Analyze page DOM to design precise triggers:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Extract forms
|
||||||
|
(function(){var forms=document.querySelectorAll('form');return JSON.stringify(Array.from(forms).map(function(f){return{id:f.id,action:f.action,fields:Array.from(f.querySelectorAll('input,textarea,select')).map(function(el){return{name:el.name,type:el.type,id:el.id}})}}));})()
|
||||||
|
|
||||||
|
// Extract CTAs and buttons
|
||||||
|
(function(){return JSON.stringify(Array.from(document.querySelectorAll('a[class*="btn"],button,[role="button"]')).map(function(el){return{text:(el.textContent||'').trim().substring(0,50),href:el.href||'',classes:el.className,id:el.id}}));})()
|
||||||
|
```
|
||||||
|
|
||||||
|
**Trigger types:**
|
||||||
|
| Type | Use For | Key Parameter |
|
||||||
|
|------|---------|---------------|
|
||||||
|
| `linkClick` | `<a>` elements | `autoEventFilter` with CSS selector |
|
||||||
|
| `click` | Any element | `filter` on Click ID/Classes |
|
||||||
|
| `formSubmission` | Form submit | `filter` on Form ID |
|
||||||
|
| `customEvent` | dataLayer events | `custom_event_filter` on `{{_event}}` |
|
||||||
|
| `scrollDepth` | Scroll tracking | `verticalThresholdsPercent` |
|
||||||
|
| `timer` | Time on page | `interval`, `limit` |
|
||||||
|
| `domReady` | DOM loaded | No filter needed |
|
||||||
|
|
||||||
|
### 3. Custom HTML Generation (ES5 MANDATORY)
|
||||||
|
|
||||||
|
GTM Custom HTML uses a JavaScript compiler that does NOT support ES2020+.
|
||||||
|
|
||||||
|
**FORBIDDEN:**
|
||||||
|
```javascript
|
||||||
|
// NO: optional chaining, arrow functions, const/let, template literals,
|
||||||
|
// destructuring, spread, async/await
|
||||||
|
```
|
||||||
|
|
||||||
|
**REQUIRED:**
|
||||||
|
```javascript
|
||||||
|
// YES: var, function(){}, string concatenation, explicit property access
|
||||||
|
(function() {
|
||||||
|
var form = document.getElementById('contact-form');
|
||||||
|
if (!form) return;
|
||||||
|
form.addEventListener('submit', function() {
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
window.dataLayer.push({
|
||||||
|
'event': 'generate_lead',
|
||||||
|
'lead_type': form.querySelector('#requestType').value
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Workspace Lifecycle Management
|
||||||
|
|
||||||
|
GTM workspaces become **read-only after publishing**. The DTM Agent auto-handles this:
|
||||||
|
- If create/update fails with "Workspace is already submitted"
|
||||||
|
- Auto-creates a new workspace
|
||||||
|
- Retries the operation
|
||||||
|
- Caches the workspace ID for subsequent calls
|
||||||
|
|
||||||
|
**You don't need to manage workspaces manually.** Just call `dtm_create_tag` etc. and it works.
|
||||||
|
|
||||||
|
### 5. DataLayer Code Generation
|
||||||
|
|
||||||
|
Generate dataLayer push code for common tracking scenarios:
|
||||||
|
|
||||||
|
**E-commerce:**
|
||||||
|
```javascript
|
||||||
|
// purchase event
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
window.dataLayer.push({ ecommerce: null }); // Clear previous
|
||||||
|
window.dataLayer.push({
|
||||||
|
'event': 'purchase',
|
||||||
|
'ecommerce': {
|
||||||
|
'transaction_id': /* order ID */,
|
||||||
|
'currency': 'KRW',
|
||||||
|
'value': /* total */,
|
||||||
|
'items': [{ item_id: '', item_name: '', price: 0, quantity: 1 }]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**Forms/Leads:**
|
||||||
|
```javascript
|
||||||
|
window.dataLayer.push({
|
||||||
|
'event': 'generate_lead',
|
||||||
|
'lead_type': /* form type */,
|
||||||
|
'form_id': /* form element ID */
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Korean Market Patterns
|
||||||
|
|
||||||
|
| Context | Pattern |
|
||||||
|
|---------|---------|
|
||||||
|
| Currency | KRW (no decimals) |
|
||||||
|
| Payment buttons | 장바구니, 결제하기, 주문하기, 문의하기 |
|
||||||
|
| Payment methods | 카카오페이, 네이버페이, 토스, 신용카드 |
|
||||||
|
| Platforms | Kakao Pixel (`track.kakao.com`), Naver Analytics (`wcs.naver.net`) |
|
||||||
|
|
||||||
|
## Supported Tag Types
|
||||||
|
|
||||||
|
| GTM Type | Name | Platform |
|
||||||
|
|----------|------|----------|
|
||||||
|
| `googtag` | Google Tag | GA4 config |
|
||||||
|
| `gaawe` | GA4 Event | GA4 events |
|
||||||
|
| `gclidw` | Conversion Linker | Google Ads |
|
||||||
|
| `awct` | Ads Conversion | Google Ads |
|
||||||
|
| `html` | Custom HTML | Any (Meta, PostHog, Kakao, etc.) |
|
||||||
|
| `cvt_MQDKZ` | Clarity | Microsoft Clarity |
|
||||||
|
| `cvt_WF3R3` | Amplitude | Amplitude |
|
||||||
|
|
||||||
|
## FB Conversions API Integration
|
||||||
|
|
||||||
|
When creating tags that should trigger FB CAPI:
|
||||||
|
1. Check existing FBEventName mapping table (Regex Table variable)
|
||||||
|
2. If your custom event isn't mapped, add a new rule: `event_name → FB_Standard_Event`
|
||||||
|
3. Key mappings: `generate_lead → Lead`, `purchase → Purchase`, `page_view → PageView`
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. **Analyze**: Use Chrome DevTools to inspect the page (or receive from gtm-audit Mode D)
|
||||||
|
2. **Design**: Determine events, triggers, variables needed
|
||||||
|
3. **Create**: Use DTM Agent MCP tools to create in GTM
|
||||||
|
4. **Verify**: Hand off to gtm-validator for QA
|
||||||
|
5. **Document**: Write implementation details to Notion
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
- Always use `dtm_status` first to verify auth and active container
|
||||||
|
- Always use `measurementIdOverride` for GA4 event tags (NOT `measurementId`)
|
||||||
|
- All Custom HTML must be ES5-compatible
|
||||||
|
- Use `--dry-run` conceptually — verify trigger selectors via Chrome DevTools before creating
|
||||||
|
- Rate limit: space API calls 1 second apart in batch operations
|
||||||
|
- Never delete tags without explicit user confirmation — move to Archive folder instead
|
||||||
|
- After creating tags, suggest user run gtm-validator to verify
|
||||||
224
custom-skills/62-gtm-validator/code/SKILL.md
Normal file
224
custom-skills/62-gtm-validator/code/SKILL.md
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
---
|
||||||
|
name: gtm-validator
|
||||||
|
description: >
|
||||||
|
GTM QA and validation toolkit. Verifies tags fire correctly on live pages,
|
||||||
|
tests trigger conditions against actual DOM, validates dataLayer schemas,
|
||||||
|
checks naming conventions, compares cross-platform events, and runs QA checklists.
|
||||||
|
Uses Chrome DevTools MCP for live page testing.
|
||||||
|
Triggers on "validate tags", "check triggers", "verify tracking", "QA GTM",
|
||||||
|
"test events", "debug GTM", "troubleshoot", "naming conventions",
|
||||||
|
"GTM question", "GTM best practice", "compare versions".
|
||||||
|
NOT for auditing (use gtm-audit) or creating/modifying (use gtm-editor).
|
||||||
|
---
|
||||||
|
|
||||||
|
# GTM Validator Skill
|
||||||
|
|
||||||
|
Verify GTM implementations work correctly on live pages. Test triggers, validate dataLayer, check naming conventions.
|
||||||
|
|
||||||
|
## Available Tools
|
||||||
|
|
||||||
|
### Browser Testing (Chrome DevTools MCP)
|
||||||
|
- `navigate_page` — Load page to test
|
||||||
|
- `evaluate_script` — Check dataLayer state, test selectors, verify globals
|
||||||
|
- `list_network_requests` — Verify tag firing after interactions
|
||||||
|
- `click` / `hover` — Simulate user actions to trigger events
|
||||||
|
- `list_console_messages` — Check for JS errors
|
||||||
|
- `take_screenshot` — Document test results
|
||||||
|
|
||||||
|
### GTM Container (DTM Agent MCP)
|
||||||
|
- `dtm_status` — Check active container
|
||||||
|
- `dtm_list_tags` / `dtm_get_tag` — Get tag configurations
|
||||||
|
- `dtm_list_triggers` / `dtm_get_trigger` — Get trigger conditions
|
||||||
|
- `dtm_list_variables` / `dtm_get_variable` — Get variable definitions
|
||||||
|
- `dtm_list_versions` / `dtm_get_live_version` — Compare versions
|
||||||
|
|
||||||
|
## Validation Modes
|
||||||
|
|
||||||
|
### 1. Tag Firing Verification
|
||||||
|
|
||||||
|
Test that specific tags fire on the correct pages and interactions.
|
||||||
|
|
||||||
|
**Steps:**
|
||||||
|
1. `dtm_list_tags` — get all active tags with their triggers
|
||||||
|
2. `navigate_page` to target URL
|
||||||
|
3. `list_network_requests` — capture initial tag firings
|
||||||
|
4. For click triggers: `click` the element, then check `list_network_requests` again
|
||||||
|
5. Compare: expected tags (from GTM config) vs actual network requests
|
||||||
|
|
||||||
|
**Verification script for specific tag:**
|
||||||
|
```javascript
|
||||||
|
// Check if GA4 event fired
|
||||||
|
(function(){
|
||||||
|
var dl = window.dataLayer || [];
|
||||||
|
var events = dl.filter(function(e){return e.event && e.event !== 'gtm.js' && e.event !== 'gtm.dom' && e.event !== 'gtm.load';});
|
||||||
|
return JSON.stringify(events.map(function(e){return {event: e.event, params: Object.keys(e).filter(function(k){return k !== 'event' && !k.startsWith('gtm.')})}}));
|
||||||
|
})()
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Trigger Condition Testing
|
||||||
|
|
||||||
|
Verify trigger CSS selectors and conditions match the actual page DOM.
|
||||||
|
|
||||||
|
**Steps:**
|
||||||
|
1. `dtm_get_trigger` — get trigger filter/autoEventFilter
|
||||||
|
2. Extract CSS selectors from trigger config
|
||||||
|
3. `evaluate_script` — test if selectors match elements on page:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Test CSS selector
|
||||||
|
(function(selector){
|
||||||
|
var els = document.querySelectorAll(selector);
|
||||||
|
return JSON.stringify({
|
||||||
|
selector: selector,
|
||||||
|
matchCount: els.length,
|
||||||
|
elements: Array.from(els).slice(0,5).map(function(el){
|
||||||
|
return {tag: el.tagName, id: el.id, classes: el.className, text: (el.textContent||'').trim().substring(0,30)};
|
||||||
|
})
|
||||||
|
});
|
||||||
|
})('.hero a.btn')
|
||||||
|
```
|
||||||
|
|
||||||
|
4. If selector matches 0 elements → trigger will NEVER fire → **CRITICAL issue**
|
||||||
|
5. If selector matches too many elements → trigger may fire incorrectly → **WARNING**
|
||||||
|
|
||||||
|
### 3. DataLayer Schema Validation
|
||||||
|
|
||||||
|
Verify dataLayer pushes contain required fields with correct types.
|
||||||
|
|
||||||
|
**GA4 Recommended Event Schemas:**
|
||||||
|
|
||||||
|
| Event | Required Fields |
|
||||||
|
|-------|----------------|
|
||||||
|
| `page_view` | (automatic) |
|
||||||
|
| `generate_lead` | — (value, currency optional) |
|
||||||
|
| `purchase` | transaction_id, value, currency, items[] |
|
||||||
|
| `add_to_cart` | items[] (item_id, item_name, price, quantity) |
|
||||||
|
| `form_start` | form_id, form_name |
|
||||||
|
| `form_submit` | form_id, form_name |
|
||||||
|
|
||||||
|
**Validation script:**
|
||||||
|
```javascript
|
||||||
|
// Check dataLayer for specific event schema
|
||||||
|
(function(eventName){
|
||||||
|
var dl = window.dataLayer || [];
|
||||||
|
var events = dl.filter(function(e){return e.event === eventName;});
|
||||||
|
if(events.length === 0) return JSON.stringify({found: false, event: eventName});
|
||||||
|
var e = events[events.length-1];
|
||||||
|
return JSON.stringify({found: true, event: eventName, keys: Object.keys(e), data: e});
|
||||||
|
})('generate_lead')
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Naming Convention Check
|
||||||
|
|
||||||
|
Verify all GTM resources follow D.intelligence naming standards.
|
||||||
|
|
||||||
|
**Tag naming:**
|
||||||
|
```
|
||||||
|
[Platform] - [event_name] [context]
|
||||||
|
Examples:
|
||||||
|
GA4 - generate_lead (contact form)
|
||||||
|
sGTM - purchase
|
||||||
|
cHTML - Contact Form dataLayer Events
|
||||||
|
FB_CONVERSIONS_API-PIXEL_ID-Web-Tag-GA4_Event
|
||||||
|
```
|
||||||
|
|
||||||
|
**Trigger naming:**
|
||||||
|
```
|
||||||
|
[Type] - [description] Trigger
|
||||||
|
Examples:
|
||||||
|
Click - Hero CTA (consult)
|
||||||
|
CE - contact_form_submit
|
||||||
|
Form Submit - contact-form
|
||||||
|
Scroll - 50pct Depth
|
||||||
|
PV - consult booking Trigger
|
||||||
|
```
|
||||||
|
|
||||||
|
**Variable naming:**
|
||||||
|
```
|
||||||
|
[prefix] - [description]
|
||||||
|
Prefixes: dlv (dataLayer), cjs (Custom JS), aev (Auto-Event),
|
||||||
|
jsv (JS Variable), URL query, cookie, c (Constant)
|
||||||
|
Examples:
|
||||||
|
dlv - contact_form.request_type
|
||||||
|
cjs - Social platform
|
||||||
|
aev - click url hostname
|
||||||
|
```
|
||||||
|
|
||||||
|
**Validation steps:**
|
||||||
|
1. `dtm_list_tags` / `dtm_list_triggers` / `dtm_list_variables`
|
||||||
|
2. Check each name against patterns above
|
||||||
|
3. Flag violations: missing prefix, wrong case, unclear description
|
||||||
|
|
||||||
|
### 5. Cross-Platform Event Mapping Verification
|
||||||
|
|
||||||
|
Verify the same user action sends consistent events to all platforms.
|
||||||
|
|
||||||
|
| User Action | GA4 | Meta (FB) | Google Ads | Kakao | Naver |
|
||||||
|
|------------|-----|-----------|------------|-------|-------|
|
||||||
|
| Page view | page_view | PageView | — | pageView | — |
|
||||||
|
| Product view | view_item | ViewContent | — | viewContent | — |
|
||||||
|
| Add to cart | add_to_cart | AddToCart | — | addToCart | — |
|
||||||
|
| Purchase | purchase | Purchase | purchase | purchase | — |
|
||||||
|
| Lead form | generate_lead | Lead | submit_lead_form | participation | — |
|
||||||
|
|
||||||
|
**Steps:**
|
||||||
|
1. Trigger a user action (click, form submit)
|
||||||
|
2. `list_network_requests` — capture all resulting requests
|
||||||
|
3. Check each platform received the correct event:
|
||||||
|
- GA4: `google-analytics.com/g/collect?en=...`
|
||||||
|
- Meta: `facebook.com/tr/?ev=...`
|
||||||
|
- Google Ads: `googleads.g.doubleclick.net/pagead/...`
|
||||||
|
|
||||||
|
### 6. QA Checklist Execution
|
||||||
|
|
||||||
|
Run through a standard QA checklist for GTM implementation:
|
||||||
|
|
||||||
|
- [ ] GTM container snippet in `<head>` (not `<body>`)
|
||||||
|
- [ ] GTM noscript fallback in `<body>`
|
||||||
|
- [ ] No duplicate GTM containers on same page
|
||||||
|
- [ ] dataLayer initialized before GTM snippet
|
||||||
|
- [ ] All P1 tags fire on page load (GA4 config, Consent Mode)
|
||||||
|
- [ ] Click triggers use specific selectors (not generic classes)
|
||||||
|
- [ ] Form triggers match form IDs that exist on page
|
||||||
|
- [ ] Custom HTML tags use ES5 syntax only
|
||||||
|
- [ ] No console errors related to GTM/tags
|
||||||
|
- [ ] Consent Mode properly blocks tags before consent
|
||||||
|
- [ ] sGTM endpoint responding (no SSL errors)
|
||||||
|
- [ ] Cross-domain tracking configured if needed
|
||||||
|
|
||||||
|
### 7. Version Comparison
|
||||||
|
|
||||||
|
Compare tags between two container versions to identify changes.
|
||||||
|
|
||||||
|
1. `dtm_list_versions` — get version list
|
||||||
|
2. `dtm_get_live_version` — get current live version with all tags
|
||||||
|
3. Compare tag counts, trigger counts, new/modified/deleted items
|
||||||
|
4. Report changes since last publish
|
||||||
|
|
||||||
|
## Reference Files
|
||||||
|
|
||||||
|
| File | Content |
|
||||||
|
|------|---------|
|
||||||
|
| `references/datalayer-schema.md` | GA4 recommended event schemas |
|
||||||
|
| `references/platform-mapping.md` | GA4 ↔ Meta ↔ Kakao ↔ Ads event mapping |
|
||||||
|
| `references/naming-conventions.md` | Tag/trigger/variable naming rules |
|
||||||
|
| `references/qa-checklist.md` | Full QA checklist |
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. **Receive** tag list from gtm-editor (or run `dtm_list_tags`)
|
||||||
|
2. **Navigate** to the target page via Chrome DevTools
|
||||||
|
3. **Test** each trigger's CSS selector matches actual DOM elements
|
||||||
|
4. **Simulate** user actions (clicks, form submits, scrolls)
|
||||||
|
5. **Capture** network requests after each action
|
||||||
|
6. **Verify** correct events fired to correct platforms
|
||||||
|
7. **Report** pass/fail with evidence (screenshots, network logs)
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
- Always test on the LIVE published version, not preview (unless debugging)
|
||||||
|
- Test on both desktop and mobile viewports when possible
|
||||||
|
- Check consent mode state before flagging missing tags
|
||||||
|
- Document every test result with screenshots or network request evidence
|
||||||
|
- If a trigger selector doesn't match, suggest a fix and hand off to gtm-editor
|
||||||
|
- GTM questions, best practices, and architecture advice are also in scope
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
# DataLayer Schema Reference
|
||||||
|
|
||||||
|
GA4 권장 이벤트 기반 DataLayer 스키마 정의.
|
||||||
|
|
||||||
|
## Core Principles
|
||||||
|
|
||||||
|
1. GTM 스니펫 이전에 dataLayer 초기화
|
||||||
|
2. 이벤트 발생 시점에 push (사전 아님)
|
||||||
|
3. ecommerce 객체는 push 전 clear 필수
|
||||||
|
4. PII 데이터 직접 수집 금지
|
||||||
|
|
||||||
|
## Base Structure
|
||||||
|
|
||||||
|
### Page Load
|
||||||
|
```javascript
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
dataLayer.push({
|
||||||
|
'event': 'page_data',
|
||||||
|
'page': {
|
||||||
|
'type': 'home|category|product|cart|checkout|thankyou|content|search',
|
||||||
|
'name': 'Page Name',
|
||||||
|
'category': 'Category/Subcategory',
|
||||||
|
'language': 'ko'
|
||||||
|
},
|
||||||
|
'user': {
|
||||||
|
'status': 'logged_in|logged_out',
|
||||||
|
'id': 'hashed_user_id', // SHA256, 로그인 시에만
|
||||||
|
'type': 'new|returning|member|guest',
|
||||||
|
'loginMethod': 'email|kakao|naver|google'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## E-commerce Events
|
||||||
|
|
||||||
|
### view_item
|
||||||
|
```javascript
|
||||||
|
dataLayer.push({ ecommerce: null });
|
||||||
|
dataLayer.push({
|
||||||
|
'event': 'view_item',
|
||||||
|
'ecommerce': {
|
||||||
|
'currency': 'KRW',
|
||||||
|
'value': 29000,
|
||||||
|
'items': [{
|
||||||
|
'item_id': 'SKU_12345',
|
||||||
|
'item_name': 'Product Name',
|
||||||
|
'item_brand': 'Brand Name',
|
||||||
|
'item_category': 'Category',
|
||||||
|
'item_variant': 'Blue / Large',
|
||||||
|
'price': 29000,
|
||||||
|
'quantity': 1
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### add_to_cart
|
||||||
|
```javascript
|
||||||
|
dataLayer.push({ ecommerce: null });
|
||||||
|
dataLayer.push({
|
||||||
|
'event': 'add_to_cart',
|
||||||
|
'ecommerce': {
|
||||||
|
'currency': 'KRW',
|
||||||
|
'value': 29000,
|
||||||
|
'items': [{
|
||||||
|
'item_id': 'SKU_12345',
|
||||||
|
'item_name': 'Product Name',
|
||||||
|
'price': 29000,
|
||||||
|
'quantity': 1
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### begin_checkout
|
||||||
|
```javascript
|
||||||
|
dataLayer.push({ ecommerce: null });
|
||||||
|
dataLayer.push({
|
||||||
|
'event': 'begin_checkout',
|
||||||
|
'ecommerce': {
|
||||||
|
'currency': 'KRW',
|
||||||
|
'value': 58000,
|
||||||
|
'coupon': 'WELCOME10',
|
||||||
|
'items': [/* cart items */]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### purchase
|
||||||
|
```javascript
|
||||||
|
dataLayer.push({ ecommerce: null });
|
||||||
|
dataLayer.push({
|
||||||
|
'event': 'purchase',
|
||||||
|
'ecommerce': {
|
||||||
|
'transaction_id': 'T_20250115_001',
|
||||||
|
'affiliation': 'Store Name',
|
||||||
|
'value': 53000,
|
||||||
|
'tax': 4818,
|
||||||
|
'shipping': 3000,
|
||||||
|
'currency': 'KRW',
|
||||||
|
'coupon': 'WELCOME10',
|
||||||
|
'items': [{
|
||||||
|
'item_id': 'SKU_12345',
|
||||||
|
'item_name': 'Product Name',
|
||||||
|
'price': 29000,
|
||||||
|
'quantity': 1
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lead Gen Events
|
||||||
|
|
||||||
|
### generate_lead
|
||||||
|
```javascript
|
||||||
|
dataLayer.push({
|
||||||
|
'event': 'generate_lead',
|
||||||
|
'currency': 'KRW',
|
||||||
|
'value': 50000,
|
||||||
|
'lead': {
|
||||||
|
'source': 'contact_form|landing_page',
|
||||||
|
'type': 'inquiry|quote_request'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### form_submit
|
||||||
|
```javascript
|
||||||
|
dataLayer.push({
|
||||||
|
'event': 'form_submit',
|
||||||
|
'form': {
|
||||||
|
'id': 'contact_form',
|
||||||
|
'name': 'Contact Us Form',
|
||||||
|
'type': 'contact',
|
||||||
|
'success': true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Engagement Events
|
||||||
|
|
||||||
|
### login / sign_up
|
||||||
|
```javascript
|
||||||
|
dataLayer.push({
|
||||||
|
'event': 'login', // or 'sign_up'
|
||||||
|
'method': 'email|kakao|naver|google'
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### search
|
||||||
|
```javascript
|
||||||
|
dataLayer.push({
|
||||||
|
'event': 'search',
|
||||||
|
'search_term': 'user search query',
|
||||||
|
'search': {
|
||||||
|
'results_count': 42,
|
||||||
|
'category': 'product|content'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Item Object Reference
|
||||||
|
|
||||||
|
| Parameter | Type | Required | Description |
|
||||||
|
|-----------|------|----------|-------------|
|
||||||
|
| item_id | string | Yes | SKU |
|
||||||
|
| item_name | string | Yes | 상품명 |
|
||||||
|
| price | number | Yes | 단가 |
|
||||||
|
| quantity | number | Yes | 수량 |
|
||||||
|
| item_brand | string | No | 브랜드명 |
|
||||||
|
| item_category | string | No | 카테고리 |
|
||||||
|
| item_category2-5 | string | No | 하위 카테고리 |
|
||||||
|
| item_variant | string | No | 옵션 |
|
||||||
|
| coupon | string | No | 쿠폰 코드 |
|
||||||
|
| discount | number | No | 할인 금액 |
|
||||||
|
| index | number | No | 목록 위치 |
|
||||||
|
|
||||||
|
## Validation Rules
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ✅ Correct
|
||||||
|
'price': 29000 // number
|
||||||
|
'quantity': 1 // number
|
||||||
|
'currency': 'KRW' // uppercase
|
||||||
|
|
||||||
|
// ❌ Wrong
|
||||||
|
'price': '29000' // string
|
||||||
|
'currency': 'krw' // lowercase
|
||||||
|
```
|
||||||
|
|
||||||
|
### Array Limits
|
||||||
|
- items[]: 최대 200개
|
||||||
|
- 초과 시 분할 전송
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
# GTM Naming Conventions
|
||||||
|
|
||||||
|
일관된 GTM 컨테이너 관리를 위한 네이밍 규칙.
|
||||||
|
|
||||||
|
## Tag Naming
|
||||||
|
|
||||||
|
### Pattern
|
||||||
|
```
|
||||||
|
[Platform] - [Type] - [Description]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
| Platform | Type | Description | Tag Name |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| GA4 | Config | Main | GA4 - Config - Main |
|
||||||
|
| GA4 | Event | purchase | GA4 - Event - purchase |
|
||||||
|
| GAds | Conversion | Purchase | GAds - Conversion - Purchase |
|
||||||
|
| Meta | Event | Purchase | Meta - Event - Purchase |
|
||||||
|
| Kakao | Event | purchase | Kakao - Event - purchase |
|
||||||
|
|
||||||
|
## Trigger Naming
|
||||||
|
|
||||||
|
### Pattern
|
||||||
|
```
|
||||||
|
[Type] - [Condition] - [Context]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Type Codes
|
||||||
|
| Code | Type | Example |
|
||||||
|
|------|------|---------|
|
||||||
|
| PV | Page View | PV - All Pages |
|
||||||
|
| DL | DataLayer Event | DL - purchase |
|
||||||
|
| Click | Click | Click - CTA Button |
|
||||||
|
| Form | Form Submit | Form - Contact |
|
||||||
|
| Scroll | Scroll Depth | Scroll - 75% |
|
||||||
|
| Vis | Element Visibility | Vis - Video Player |
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
```
|
||||||
|
PV - All Pages
|
||||||
|
PV - Thank You Page
|
||||||
|
DL - purchase
|
||||||
|
DL - add_to_cart
|
||||||
|
Click - CTA Button - Hero
|
||||||
|
Form - Contact Form
|
||||||
|
Scroll - 90%
|
||||||
|
```
|
||||||
|
|
||||||
|
## Variable Naming
|
||||||
|
|
||||||
|
### Pattern
|
||||||
|
```
|
||||||
|
[Type] - [Name/Path]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Type Codes
|
||||||
|
| Code | Type | Example |
|
||||||
|
|------|------|---------|
|
||||||
|
| DLV | DataLayer Variable | DLV - ecommerce.value |
|
||||||
|
| CJS | Custom JavaScript | CJS - Item IDs Array |
|
||||||
|
| Const | Constant | Const - GA4 Measurement ID |
|
||||||
|
| LT | Lookup Table | LT - Page Type Mapping |
|
||||||
|
| URL | URL Variable | URL - Path |
|
||||||
|
| DOM | DOM Element | DOM - Product Title |
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
```
|
||||||
|
DLV - event
|
||||||
|
DLV - ecommerce.transaction_id
|
||||||
|
DLV - ecommerce.items
|
||||||
|
CJS - Items for Meta
|
||||||
|
CJS - Formatted Price
|
||||||
|
Const - GA4 Measurement ID
|
||||||
|
Const - Meta Pixel ID
|
||||||
|
LT - Payment Method Mapping
|
||||||
|
```
|
||||||
|
|
||||||
|
## Folder Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
📁 01. Configuration
|
||||||
|
📁 02. GA4 Events
|
||||||
|
📁 03. Google Ads
|
||||||
|
📁 04. Meta Pixel
|
||||||
|
📁 05. Kakao Pixel
|
||||||
|
📁 06. Other Platforms
|
||||||
|
📁 07. Utilities
|
||||||
|
📁 99. Deprecated
|
||||||
|
```
|
||||||
|
|
||||||
|
## Built-in Variables to Enable
|
||||||
|
|
||||||
|
```
|
||||||
|
Pages: ✅ Page URL, Path, Hostname, Referrer
|
||||||
|
Utilities: ✅ Event, Container ID, Random Number
|
||||||
|
Clicks: ✅ Element, Classes, ID, URL, Text
|
||||||
|
Forms: ✅ Element, Classes, ID, URL, Text
|
||||||
|
History: ✅ New/Old History Fragment/State
|
||||||
|
Videos: ✅ All YouTube variables
|
||||||
|
Scrolling: ✅ Depth Threshold, Units, Direction
|
||||||
|
Visibility: ✅ Percent Visible, On-Screen Duration
|
||||||
|
```
|
||||||
|
|
||||||
|
## Anti-patterns
|
||||||
|
|
||||||
|
```
|
||||||
|
❌ Tag 1, New Tag, Test
|
||||||
|
❌ GA4 purchase event (일관성 없음)
|
||||||
|
❌ G4-E-Pch (과도한 약어)
|
||||||
|
❌ Very Long Tag Name for Purchase Complete Event (너무 김)
|
||||||
|
|
||||||
|
✅ GA4 - Event - purchase
|
||||||
|
✅ DL - purchase
|
||||||
|
✅ CJS - Items for Meta
|
||||||
|
```
|
||||||
|
|
||||||
|
## Version Notes
|
||||||
|
|
||||||
|
```
|
||||||
|
v[Major].[Minor] - [Description]
|
||||||
|
예: v12.1 - Added form_submit event
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
- Added:
|
||||||
|
- Modified:
|
||||||
|
- Removed:
|
||||||
|
|
||||||
|
## Reason
|
||||||
|
[변경 이유]
|
||||||
|
```
|
||||||
215
custom-skills/62-gtm-validator/code/references/phase6-audit.md
Normal file
215
custom-skills/62-gtm-validator/code/references/phase6-audit.md
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
# Phase 6: Progressive Audit & Update
|
||||||
|
|
||||||
|
설정된 GTM Container에 대한 주기적 진단, 유효성 검증, 마이너 업데이트 및 태그 추가.
|
||||||
|
|
||||||
|
## Objectives
|
||||||
|
|
||||||
|
1. GTM 컨테이너 건강 상태 점검
|
||||||
|
2. 태그 발화 및 데이터 정합성 검증
|
||||||
|
3. 최신 요구사항 반영 업데이트
|
||||||
|
4. D.intelligence GTM Toolkit 연계 자동화
|
||||||
|
|
||||||
|
## Audit Schedule
|
||||||
|
|
||||||
|
| Frequency | Scope | Trigger |
|
||||||
|
|-----------|-------|---------|
|
||||||
|
| Weekly | Tag firing validation | 자동 스케줄 |
|
||||||
|
| Monthly | Full container review | 월간 리포트 |
|
||||||
|
| Quarterly | Architecture review | 분기 회의 |
|
||||||
|
| Ad-hoc | Issue investigation | 이슈 발생 시 |
|
||||||
|
|
||||||
|
## Audit Checklist
|
||||||
|
|
||||||
|
### 1. Container Health Check
|
||||||
|
- [ ] 버전 히스토리 확인 (최근 변경사항)
|
||||||
|
- [ ] 미사용 태그/트리거/변수 식별
|
||||||
|
- [ ] 명명 규칙 준수 여부
|
||||||
|
- [ ] 폴더 구조 정리 상태
|
||||||
|
|
||||||
|
### 2. Tag Firing Validation
|
||||||
|
- [ ] 모든 P1 태그 정상 발화
|
||||||
|
- [ ] 트리거 조건 정확성
|
||||||
|
- [ ] 변수 값 정확성
|
||||||
|
- [ ] 차단 트리거 작동
|
||||||
|
|
||||||
|
### 3. Data Quality
|
||||||
|
- [ ] GA4 Realtime 데이터 확인
|
||||||
|
- [ ] 전환 데이터 정합성
|
||||||
|
- [ ] 파라미터 값 검증
|
||||||
|
- [ ] 이상치 탐지
|
||||||
|
|
||||||
|
### 4. Platform Sync
|
||||||
|
- [ ] GA4 ↔ GTM 동기화
|
||||||
|
- [ ] Google Ads 전환 데이터
|
||||||
|
- [ ] Meta Events Manager 상태
|
||||||
|
- [ ] Kakao Pixel 상태
|
||||||
|
|
||||||
|
## D.intelligence GTM Toolkit Integration
|
||||||
|
|
||||||
|
### Repository
|
||||||
|
```
|
||||||
|
https://github.com/ourdigital/dintel-gtm-toolkit
|
||||||
|
```
|
||||||
|
|
||||||
|
### Capabilities
|
||||||
|
|
||||||
|
| Feature | Description | Use Case |
|
||||||
|
|---------|-------------|----------|
|
||||||
|
| Container Analysis | JSON 파싱, 구조 분석 | 컨테이너 리뷰 |
|
||||||
|
| Dependency Mapping | 태그↔트리거↔변수 관계 | 영향도 분석 |
|
||||||
|
| Version Diff | 버전 간 변경사항 비교 | 변경 추적 |
|
||||||
|
| Tag Validation | 발화 상태 자동 검증 | 정기 감사 |
|
||||||
|
|
||||||
|
### Integration Workflow
|
||||||
|
|
||||||
|
```
|
||||||
|
1. GTM Container Export
|
||||||
|
└── JSON 파일 다운로드
|
||||||
|
|
||||||
|
2. D.intel Toolkit Analysis
|
||||||
|
├── python analyze_container.py container.json
|
||||||
|
├── Dependency graph 생성
|
||||||
|
└── Issue detection
|
||||||
|
|
||||||
|
3. Report Generation
|
||||||
|
└── Markdown/HTML 리포트
|
||||||
|
|
||||||
|
4. GTM Guardian Update
|
||||||
|
└── Notion에 리포트 저장
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sample Toolkit Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Container 분석
|
||||||
|
python analyze_container.py GTM-XXXXXX.json --output report.md
|
||||||
|
|
||||||
|
# 버전 비교
|
||||||
|
python diff_versions.py v1.json v2.json --output diff.md
|
||||||
|
|
||||||
|
# 태그 검증
|
||||||
|
python validate_tags.py container.json --events events.csv
|
||||||
|
|
||||||
|
# 미사용 요소 탐지
|
||||||
|
python find_unused.py container.json --type all
|
||||||
|
```
|
||||||
|
|
||||||
|
## Audit Report Template
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# GTM Audit Report
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
- **Container**: GTM-XXXXXX
|
||||||
|
- **Audit Date**: YYYY-MM-DD
|
||||||
|
- **Version Reviewed**: XX
|
||||||
|
- **Overall Status**: ✅ Healthy / ⚠️ Needs Attention / ❌ Critical
|
||||||
|
|
||||||
|
## Findings
|
||||||
|
|
||||||
|
### ✅ Passing
|
||||||
|
- [항목]
|
||||||
|
|
||||||
|
### ⚠️ Warnings
|
||||||
|
| Issue | Impact | Recommendation |
|
||||||
|
|-------|--------|----------------|
|
||||||
|
|
||||||
|
### ❌ Critical
|
||||||
|
| Issue | Impact | Action Required |
|
||||||
|
|-------|--------|-----------------|
|
||||||
|
|
||||||
|
## Tag Firing Status
|
||||||
|
|
||||||
|
| Tag | Expected | Actual | Status |
|
||||||
|
|-----|----------|--------|--------|
|
||||||
|
| GA4 - Event - purchase | Thank You | Thank You | ✅ |
|
||||||
|
|
||||||
|
## Data Quality Check
|
||||||
|
|
||||||
|
| Metric | Expected | Actual | Variance |
|
||||||
|
|--------|----------|--------|----------|
|
||||||
|
| Daily Purchases | ~50 | 48 | -4% |
|
||||||
|
|
||||||
|
## Recommendations
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
- [ ]
|
||||||
|
- [ ]
|
||||||
|
|
||||||
|
## Appendix
|
||||||
|
- Container JSON snapshot
|
||||||
|
- D.intel Toolkit output
|
||||||
|
```
|
||||||
|
|
||||||
|
## Update Procedures
|
||||||
|
|
||||||
|
### Minor Update (태그 수정)
|
||||||
|
```
|
||||||
|
1. 변경 필요 태그 식별
|
||||||
|
2. Preview 모드에서 수정
|
||||||
|
3. 테스트 완료
|
||||||
|
4. Publish with version note
|
||||||
|
5. Notion 문서 업데이트
|
||||||
|
```
|
||||||
|
|
||||||
|
### Major Update (신규 태그 추가)
|
||||||
|
```
|
||||||
|
1. Phase 3-4 문서 업데이트
|
||||||
|
2. DataLayer 요구사항 개발팀 전달
|
||||||
|
3. GTM 태그/트리거/변수 설정
|
||||||
|
4. QA 체크리스트 수행
|
||||||
|
5. 단계적 배포
|
||||||
|
6. Notion 문서 업데이트
|
||||||
|
```
|
||||||
|
|
||||||
|
### Emergency Fix
|
||||||
|
```
|
||||||
|
1. 이슈 원인 파악
|
||||||
|
2. 최소 변경으로 수정
|
||||||
|
3. Publish (emergency version note)
|
||||||
|
4. 근본 원인 분석
|
||||||
|
5. 영구 수정 계획
|
||||||
|
```
|
||||||
|
|
||||||
|
## Versioning Best Practices
|
||||||
|
|
||||||
|
### Version Naming
|
||||||
|
```
|
||||||
|
v[Major].[Minor] - [Description]
|
||||||
|
예: v12.1 - Added form_submit event
|
||||||
|
```
|
||||||
|
|
||||||
|
### Version Notes Template
|
||||||
|
```
|
||||||
|
## Changes
|
||||||
|
- Added: [새로 추가된 항목]
|
||||||
|
- Modified: [수정된 항목]
|
||||||
|
- Removed: [삭제된 항목]
|
||||||
|
|
||||||
|
## Reason
|
||||||
|
[변경 이유]
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
- [테스트 항목 및 결과]
|
||||||
|
|
||||||
|
## Rollback Plan
|
||||||
|
[롤백 필요 시 절차]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Monitoring Setup
|
||||||
|
|
||||||
|
### GA4 Alerts
|
||||||
|
- 일일 전환 수 급감 (>30%)
|
||||||
|
- 세션 수 급감 (>50%)
|
||||||
|
- 이벤트 수집 오류
|
||||||
|
|
||||||
|
### Custom Monitoring
|
||||||
|
- DataLayer 오류 로깅
|
||||||
|
- Tag Manager 오류 추적
|
||||||
|
- 서버 사이드 태그 상태 (sGTM 시)
|
||||||
|
|
||||||
|
## Next Phase
|
||||||
|
|
||||||
|
주기적 감사 완료 후 → Phase 7: Lookup App (필요 시)
|
||||||
@@ -0,0 +1,283 @@
|
|||||||
|
# Phase 7: Event Lookup & Parameter Library Web App
|
||||||
|
|
||||||
|
Google Apps Script를 활용한 Event Taxonomy Sheet 기반 업무 지원용 앱 배포.
|
||||||
|
|
||||||
|
## Objectives
|
||||||
|
|
||||||
|
1. Event Taxonomy 데이터 조회 앱
|
||||||
|
2. 실시간 파라미터 참조 도구
|
||||||
|
3. 비개발자용 Self-service 도구
|
||||||
|
4. 팀 간 일관된 이벤트 정보 공유
|
||||||
|
|
||||||
|
## App Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||||
|
│ Google Sheets │────▶│ Apps Script │────▶│ Web App │
|
||||||
|
│ (Taxonomy DB) │ │ (Backend) │ │ (Frontend) │
|
||||||
|
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Google Sheets Data Source
|
||||||
|
|
||||||
|
### Required Sheets
|
||||||
|
- Events Master
|
||||||
|
- Parameters Reference
|
||||||
|
- Custom Definitions
|
||||||
|
- Platform Mapping
|
||||||
|
|
||||||
|
### Column Requirements
|
||||||
|
→ Phase 4 참조: [phase4-taxonomy.md](phase4-taxonomy.md)
|
||||||
|
|
||||||
|
## Apps Script Setup
|
||||||
|
|
||||||
|
### 1. Project Creation
|
||||||
|
```
|
||||||
|
1. Google Sheets 열기
|
||||||
|
2. Extensions > Apps Script
|
||||||
|
3. 프로젝트 이름 설정
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Core Functions
|
||||||
|
|
||||||
|
#### Data Retrieval
|
||||||
|
```javascript
|
||||||
|
function getEventsData() {
|
||||||
|
const sheet = SpreadsheetApp.getActiveSpreadsheet()
|
||||||
|
.getSheetByName('Events Master');
|
||||||
|
const data = sheet.getDataRange().getValues();
|
||||||
|
const headers = data[0];
|
||||||
|
|
||||||
|
return data.slice(1).map(row => {
|
||||||
|
const obj = {};
|
||||||
|
headers.forEach((header, i) => {
|
||||||
|
obj[header] = row[i];
|
||||||
|
});
|
||||||
|
return obj;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getParametersData() {
|
||||||
|
const sheet = SpreadsheetApp.getActiveSpreadsheet()
|
||||||
|
.getSheetByName('Parameters Reference');
|
||||||
|
const data = sheet.getDataRange().getValues();
|
||||||
|
const headers = data[0];
|
||||||
|
|
||||||
|
return data.slice(1).map(row => {
|
||||||
|
const obj = {};
|
||||||
|
headers.forEach((header, i) => {
|
||||||
|
obj[header] = row[i];
|
||||||
|
});
|
||||||
|
return obj;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Search Function
|
||||||
|
```javascript
|
||||||
|
function searchEvents(query) {
|
||||||
|
const events = getEventsData();
|
||||||
|
const searchTerm = query.toLowerCase();
|
||||||
|
|
||||||
|
return events.filter(event =>
|
||||||
|
event.event_name.toLowerCase().includes(searchTerm) ||
|
||||||
|
event.event_category.toLowerCase().includes(searchTerm) ||
|
||||||
|
event.notes.toLowerCase().includes(searchTerm)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEventDetails(eventName) {
|
||||||
|
const events = getEventsData();
|
||||||
|
const params = getParametersData();
|
||||||
|
|
||||||
|
const event = events.find(e => e.event_name === eventName);
|
||||||
|
if (!event) return null;
|
||||||
|
|
||||||
|
const eventParams = event.parameters.split(',').map(p => p.trim());
|
||||||
|
const paramDetails = params.filter(p =>
|
||||||
|
eventParams.includes(p.parameter_name)
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
event: event,
|
||||||
|
parameters: paramDetails
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Web App Handler
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function doGet(e) {
|
||||||
|
return HtmlService.createHtmlOutputFromFile('index')
|
||||||
|
.setTitle('GTM Event Lookup')
|
||||||
|
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
function doPost(e) {
|
||||||
|
const action = e.parameter.action;
|
||||||
|
|
||||||
|
switch(action) {
|
||||||
|
case 'search':
|
||||||
|
return ContentService.createTextOutput(
|
||||||
|
JSON.stringify(searchEvents(e.parameter.query))
|
||||||
|
).setMimeType(ContentService.MimeType.JSON);
|
||||||
|
|
||||||
|
case 'details':
|
||||||
|
return ContentService.createTextOutput(
|
||||||
|
JSON.stringify(getEventDetails(e.parameter.eventName))
|
||||||
|
).setMimeType(ContentService.MimeType.JSON);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return ContentService.createTextOutput(
|
||||||
|
JSON.stringify({error: 'Invalid action'})
|
||||||
|
).setMimeType(ContentService.MimeType.JSON);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. HTML Frontend (index.html)
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<base target="_top">
|
||||||
|
<style>
|
||||||
|
body { font-family: 'Roboto', sans-serif; padding: 20px; max-width: 800px; margin: 0 auto; }
|
||||||
|
.search-box { width: 100%; padding: 12px; font-size: 16px; margin-bottom: 20px; }
|
||||||
|
.event-card { border: 1px solid #ddd; padding: 16px; margin: 10px 0; border-radius: 8px; }
|
||||||
|
.event-name { font-size: 18px; font-weight: bold; color: #1a73e8; }
|
||||||
|
.param-badge { background: #e8f0fe; color: #1967d2; padding: 4px 8px; border-radius: 4px; margin: 2px; display: inline-block; }
|
||||||
|
.category { color: #5f6368; font-size: 14px; }
|
||||||
|
.priority-p1 { border-left: 4px solid #ea4335; }
|
||||||
|
.priority-p2 { border-left: 4px solid #fbbc04; }
|
||||||
|
.priority-p3 { border-left: 4px solid #34a853; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>🏷️ GTM Event Lookup</h1>
|
||||||
|
<input type="text" class="search-box" id="searchInput" placeholder="이벤트명 또는 카테고리 검색...">
|
||||||
|
<div id="results"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const searchInput = document.getElementById('searchInput');
|
||||||
|
const results = document.getElementById('results');
|
||||||
|
|
||||||
|
searchInput.addEventListener('input', debounce(function() {
|
||||||
|
const query = this.value;
|
||||||
|
if (query.length < 2) {
|
||||||
|
results.innerHTML = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
google.script.run
|
||||||
|
.withSuccessHandler(displayResults)
|
||||||
|
.searchEvents(query);
|
||||||
|
}, 300));
|
||||||
|
|
||||||
|
function displayResults(events) {
|
||||||
|
results.innerHTML = events.map(event => `
|
||||||
|
<div class="event-card priority-${event.priority.toLowerCase()}">
|
||||||
|
<div class="event-name">${event.event_name}</div>
|
||||||
|
<div class="category">${event.event_category} | ${event.priority}</div>
|
||||||
|
<div style="margin-top: 10px;">
|
||||||
|
${event.parameters.split(',').map(p =>
|
||||||
|
`<span class="param-badge">${p.trim()}</span>`
|
||||||
|
).join('')}
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 10px; color: #5f6368;">${event.notes}</div>
|
||||||
|
</div>
|
||||||
|
`).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
function debounce(func, wait) {
|
||||||
|
let timeout;
|
||||||
|
return function(...args) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
timeout = setTimeout(() => func.apply(this, args), wait);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
### 1. Deploy as Web App
|
||||||
|
```
|
||||||
|
1. Deploy > New deployment
|
||||||
|
2. Type: Web app
|
||||||
|
3. Execute as: Me
|
||||||
|
4. Access: Anyone within organization (또는 Anyone)
|
||||||
|
5. Deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Get Web App URL
|
||||||
|
```
|
||||||
|
배포 후 제공되는 URL 복사
|
||||||
|
https://script.google.com/macros/s/[ID]/exec
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Share with Team
|
||||||
|
- URL을 Notion/Slack에 공유
|
||||||
|
- 북마크 권장
|
||||||
|
|
||||||
|
## Features Roadmap
|
||||||
|
|
||||||
|
### Phase 1 (MVP)
|
||||||
|
- [x] 이벤트 검색
|
||||||
|
- [x] 파라미터 조회
|
||||||
|
- [x] 우선순위 필터
|
||||||
|
|
||||||
|
### Phase 2
|
||||||
|
- [ ] DataLayer 코드 스니펫 복사
|
||||||
|
- [ ] 플랫폼별 코드 예시
|
||||||
|
- [ ] 최근 조회 히스토리
|
||||||
|
|
||||||
|
### Phase 3
|
||||||
|
- [ ] 이벤트 추가/수정 폼
|
||||||
|
- [ ] 변경 이력 추적
|
||||||
|
- [ ] Slack 알림 연동
|
||||||
|
|
||||||
|
## Maintenance
|
||||||
|
|
||||||
|
### Data Update
|
||||||
|
1. Google Sheets 원본 데이터 수정
|
||||||
|
2. Web App 자동 반영 (실시간)
|
||||||
|
|
||||||
|
### Code Update
|
||||||
|
1. Apps Script 수정
|
||||||
|
2. Deploy > Manage deployments
|
||||||
|
3. New version 배포
|
||||||
|
|
||||||
|
### Backup
|
||||||
|
- Google Sheets 자동 버전 히스토리
|
||||||
|
- 주기적 수동 백업 권장
|
||||||
|
|
||||||
|
## Access Control
|
||||||
|
|
||||||
|
| Role | Access Level |
|
||||||
|
|------|--------------|
|
||||||
|
| Admin | 시트 편집 + 스크립트 수정 |
|
||||||
|
| Editor | 시트 편집 |
|
||||||
|
| Viewer | Web App 조회만 |
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
| Issue | Solution |
|
||||||
|
|-------|----------|
|
||||||
|
| 앱 로딩 느림 | 데이터 캐싱 구현 |
|
||||||
|
| 검색 결과 없음 | 검색어 확인, 데이터 존재 확인 |
|
||||||
|
| 권한 오류 | 배포 설정에서 접근 권한 확인 |
|
||||||
|
| 데이터 미반영 | 시트 새로고침, 캐시 클리어 |
|
||||||
|
|
||||||
|
## Integration with Notion
|
||||||
|
|
||||||
|
Web App URL을 Notion에 embed:
|
||||||
|
```
|
||||||
|
/embed [Web App URL]
|
||||||
|
```
|
||||||
|
|
||||||
|
또는 iframe으로 직접 삽입.
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
# Platform Event Mapping Reference
|
||||||
|
|
||||||
|
GA4, Google Ads, Meta Pixel, Kakao Pixel 간 이벤트 매핑.
|
||||||
|
|
||||||
|
## E-commerce Events
|
||||||
|
|
||||||
|
| User Action | GA4 | Meta | Kakao | Google Ads |
|
||||||
|
|-------------|-----|------|-------|------------|
|
||||||
|
| 상품 목록 조회 | view_item_list | ViewContent | viewContent | - |
|
||||||
|
| 상품 상세 조회 | view_item | ViewContent | viewContent | - |
|
||||||
|
| 상품 검색 | search | Search | search | - |
|
||||||
|
| 장바구니 담기 | add_to_cart | AddToCart | addToCart | - |
|
||||||
|
| 위시리스트 | add_to_wishlist | AddToWishlist | addToWishlist | - |
|
||||||
|
| 결제 시작 | begin_checkout | InitiateCheckout | beginCheckout | - |
|
||||||
|
| 배송정보 입력 | add_shipping_info | - | - | - |
|
||||||
|
| 결제정보 입력 | add_payment_info | AddPaymentInfo | addPaymentInfo | - |
|
||||||
|
| 구매 완료 | purchase | Purchase | purchase | purchase |
|
||||||
|
|
||||||
|
## Lead Gen Events
|
||||||
|
|
||||||
|
| User Action | GA4 | Meta | Kakao | Google Ads |
|
||||||
|
|-------------|-----|------|-------|------------|
|
||||||
|
| 회원가입 | sign_up | CompleteRegistration | completeRegistration | sign_up |
|
||||||
|
| 로그인 | login | - | login | - |
|
||||||
|
| 리드 생성 | generate_lead | Lead | participation | submit_lead_form |
|
||||||
|
|
||||||
|
## Engagement Events
|
||||||
|
|
||||||
|
| User Action | GA4 | Meta | Kakao | Google Ads |
|
||||||
|
|-------------|-----|------|-------|------------|
|
||||||
|
| 페이지 조회 | page_view | PageView | pageView | page_view |
|
||||||
|
| 검색 | search | Search | search | - |
|
||||||
|
| 공유 | share | - | - | - |
|
||||||
|
|
||||||
|
## Parameter Transformation
|
||||||
|
|
||||||
|
### GA4 → Meta
|
||||||
|
```javascript
|
||||||
|
// GA4 items[] → Meta content_ids[]
|
||||||
|
const contentIds = items.map(item => item.item_id);
|
||||||
|
|
||||||
|
// Meta Purchase
|
||||||
|
fbq('track', 'Purchase', {
|
||||||
|
content_ids: contentIds,
|
||||||
|
content_type: 'product',
|
||||||
|
value: ecommerce.value,
|
||||||
|
currency: 'KRW',
|
||||||
|
num_items: items.length
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### GA4 → Kakao
|
||||||
|
```javascript
|
||||||
|
// GA4 items[] → Kakao products[]
|
||||||
|
const products = items.map(item => ({
|
||||||
|
id: item.item_id,
|
||||||
|
name: item.item_name,
|
||||||
|
price: item.price,
|
||||||
|
quantity: item.quantity
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Kakao purchase
|
||||||
|
kakaoPixel('PIXEL_ID').purchase({
|
||||||
|
total_price: ecommerce.value,
|
||||||
|
currency: 'KRW',
|
||||||
|
products: products
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## GTM Variables
|
||||||
|
|
||||||
|
### CJS - Item IDs for Meta
|
||||||
|
```javascript
|
||||||
|
function() {
|
||||||
|
var items = {{DLV - ecommerce.items}} || [];
|
||||||
|
return items.map(function(item) {
|
||||||
|
return item.item_id;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### CJS - Products for Kakao
|
||||||
|
```javascript
|
||||||
|
function() {
|
||||||
|
var items = {{DLV - ecommerce.items}} || [];
|
||||||
|
return items.map(function(item) {
|
||||||
|
return {
|
||||||
|
id: item.item_id,
|
||||||
|
name: item.item_name,
|
||||||
|
price: item.price,
|
||||||
|
quantity: item.quantity
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deduplication
|
||||||
|
|
||||||
|
### Event ID Pattern
|
||||||
|
```javascript
|
||||||
|
var eventId = 'evt_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
||||||
|
|
||||||
|
// Client-side
|
||||||
|
fbq('track', 'Purchase', {...}, {eventID: eventId});
|
||||||
|
|
||||||
|
// Server-side (CAPI)
|
||||||
|
// 동일한 eventId 사용 → Meta 자동 중복 제거
|
||||||
|
```
|
||||||
|
|
||||||
|
## Platform-specific Notes
|
||||||
|
|
||||||
|
### Meta Pixel
|
||||||
|
- content_type: 'product' (상품) / 'product_group' (변형)
|
||||||
|
- currency: ISO 4217 필수
|
||||||
|
- value: 숫자 (소수점 가능)
|
||||||
|
|
||||||
|
### Kakao Pixel
|
||||||
|
- Server-side 미지원 (Client-only)
|
||||||
|
- products[]: 최대 10개 권장
|
||||||
|
- total_price: 숫자
|
||||||
|
|
||||||
|
### Google Ads
|
||||||
|
- Enhanced Conversions 권장
|
||||||
|
- transaction_id 필수 (중복 제거용)
|
||||||
|
- value: KRW 정수
|
||||||
149
custom-skills/62-gtm-validator/code/references/qa-checklist.md
Normal file
149
custom-skills/62-gtm-validator/code/references/qa-checklist.md
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
# GTM Implementation QA Checklist
|
||||||
|
|
||||||
|
태깅 구현 품질 검증 체크리스트.
|
||||||
|
|
||||||
|
## 1. Pre-Implementation
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
- [ ] Tagging Plan 승인 완료
|
||||||
|
- [ ] DataLayer 스키마 개발팀 공유
|
||||||
|
- [ ] 이벤트 택소노미 합의 완료
|
||||||
|
|
||||||
|
### Access
|
||||||
|
- [ ] GTM 컨테이너 접근 권한
|
||||||
|
- [ ] GA4 속성 접근 권한
|
||||||
|
- [ ] 광고 플랫폼 접근 권한
|
||||||
|
|
||||||
|
### Tools
|
||||||
|
- [ ] GTM Preview 모드 정상
|
||||||
|
- [ ] GA4 DebugView 활성화
|
||||||
|
- [ ] Chrome DevTools 준비
|
||||||
|
|
||||||
|
## 2. DataLayer Validation
|
||||||
|
|
||||||
|
### Base Setup
|
||||||
|
- [ ] GTM 스니펫 이전 dataLayer 초기화
|
||||||
|
- [ ] 페이지 로드 시 기본 데이터 push
|
||||||
|
- [ ] pageType, userId 등 기본 변수 존재
|
||||||
|
|
||||||
|
### Data Types
|
||||||
|
- [ ] 숫자 값이 Number 타입
|
||||||
|
- [ ] 금액에 통화 코드 포함 (KRW)
|
||||||
|
- [ ] transaction_id 유니크
|
||||||
|
|
||||||
|
### E-commerce
|
||||||
|
- [ ] ecommerce: null 선행 push
|
||||||
|
- [ ] items[] 배열 구조 정확
|
||||||
|
- [ ] 필수 파라미터 완비
|
||||||
|
|
||||||
|
### Console Check
|
||||||
|
```javascript
|
||||||
|
console.log(window.dataLayer);
|
||||||
|
dataLayer.filter(e => e.event === 'purchase');
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Tag Firing
|
||||||
|
|
||||||
|
### GA4
|
||||||
|
| Tag | Trigger | Status |
|
||||||
|
|-----|---------|--------|
|
||||||
|
| Config | All Pages | [ ] |
|
||||||
|
| view_item | Product page | [ ] |
|
||||||
|
| add_to_cart | Add button | [ ] |
|
||||||
|
| begin_checkout | Checkout start | [ ] |
|
||||||
|
| purchase | Thank you | [ ] |
|
||||||
|
|
||||||
|
### Google Ads
|
||||||
|
| Conversion | Trigger | Status |
|
||||||
|
|------------|---------|--------|
|
||||||
|
| Purchase | DL - purchase | [ ] |
|
||||||
|
| Lead | DL - generate_lead | [ ] |
|
||||||
|
|
||||||
|
### Meta Pixel
|
||||||
|
| Event | Trigger | Status |
|
||||||
|
|-------|---------|--------|
|
||||||
|
| PageView | All Pages | [ ] |
|
||||||
|
| ViewContent | Product | [ ] |
|
||||||
|
| AddToCart | Add | [ ] |
|
||||||
|
| Purchase | Thank you | [ ] |
|
||||||
|
|
||||||
|
### Kakao Pixel
|
||||||
|
| Event | Trigger | Status |
|
||||||
|
|-------|---------|--------|
|
||||||
|
| pageView | All Pages | [ ] |
|
||||||
|
| purchase | Thank you | [ ] |
|
||||||
|
|
||||||
|
## 4. Network Validation
|
||||||
|
|
||||||
|
### Endpoints
|
||||||
|
```
|
||||||
|
GA4: google-analytics.com/g/collect
|
||||||
|
GAds: googleads.g.doubleclick.net/pagead/conversion/
|
||||||
|
Meta: facebook.com/tr/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check Points
|
||||||
|
- [ ] 요청 전송됨
|
||||||
|
- [ ] 파라미터 정확
|
||||||
|
- [ ] 응답 성공 (200/204)
|
||||||
|
|
||||||
|
## 5. Cross-browser Testing
|
||||||
|
|
||||||
|
### Desktop
|
||||||
|
- [ ] Chrome
|
||||||
|
- [ ] Safari
|
||||||
|
- [ ] Firefox
|
||||||
|
- [ ] Edge
|
||||||
|
|
||||||
|
### Mobile
|
||||||
|
- [ ] iOS Safari
|
||||||
|
- [ ] iOS Chrome
|
||||||
|
- [ ] Android Chrome
|
||||||
|
|
||||||
|
## 6. Data Validation (Platform)
|
||||||
|
|
||||||
|
### GA4
|
||||||
|
- [ ] Realtime 보고서 이벤트 표시
|
||||||
|
- [ ] 파라미터 정확히 수집
|
||||||
|
- [ ] E-commerce 보고서 정확
|
||||||
|
|
||||||
|
### Google Ads
|
||||||
|
- [ ] 전환 액션 데이터 유입
|
||||||
|
- [ ] 전환 값 정확
|
||||||
|
|
||||||
|
### Meta
|
||||||
|
- [ ] Events Manager 수신 확인
|
||||||
|
- [ ] Event match quality 확인
|
||||||
|
|
||||||
|
## 7. Edge Cases
|
||||||
|
|
||||||
|
- [ ] 빈 장바구니 결제 시작 처리
|
||||||
|
- [ ] 0개 구매 처리
|
||||||
|
- [ ] 쿠폰 없는 구매
|
||||||
|
- [ ] 게스트 체크아웃
|
||||||
|
- [ ] 새로고침 시 중복 방지
|
||||||
|
- [ ] 뒤로가기 시 처리
|
||||||
|
|
||||||
|
## 8. Performance & Privacy
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
- [ ] 태그 로딩이 렌더링 차단 안 함
|
||||||
|
- [ ] 비동기 로딩
|
||||||
|
|
||||||
|
### Privacy
|
||||||
|
- [ ] 동의 없이 태그 미발화 (CMP 시)
|
||||||
|
- [ ] PII 직접 수집 없음
|
||||||
|
|
||||||
|
## Sign-off
|
||||||
|
|
||||||
|
| Role | Name | Date | ✓ |
|
||||||
|
|------|------|------|---|
|
||||||
|
| QA Lead | | | |
|
||||||
|
| Analytics Lead | | | |
|
||||||
|
| Dev Lead | | | |
|
||||||
|
|
||||||
|
## Issues Log
|
||||||
|
|
||||||
|
| # | Issue | Severity | Status |
|
||||||
|
|---|-------|----------|--------|
|
||||||
|
| 1 | | High/Med/Low | Open/Resolved |
|
||||||
Reference in New Issue
Block a user