refactor(skills): Restructure skills to dual-platform architecture

Major refactoring of ourdigital-custom-skills with new numbering system:

## Structure Changes
- Each skill now has code/ (Claude Code) and desktop/ (Claude Desktop) versions
- New progressive numbering: 01-09 General, 10-19 SEO, 20-29 GTM, 30-39 OurDigital, 40-49 Jamie

## Skill Reorganization
- 01-notion-organizer (from 02)
- 10-18: SEO tools split into focused skills (technical, on-page, local, schema, vitals, gsc, gateway)
- 20-21: GTM audit and manager
- 30-32: OurDigital designer, research, presentation
- 40-41: Jamie brand editor and audit

## New Files
- .claude/commands/: Slash command definitions for all skills
- CLAUDE.md: Updated with new skill structure documentation
- REFACTORING_PLAN.md: Migration documentation
- COMPATIBILITY_REPORT.md, SKILLS_COMPARISON.md: Analysis docs

## Removed
- Old skill directories (02-05, 10-14, 20-21 old numbering)
- Consolidated into new structure with _archive/ for reference

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-22 01:58:24 +09:00
parent 214247ace2
commit eea49f9f8c
251 changed files with 12308 additions and 102 deletions

View File

@@ -0,0 +1,237 @@
# E-commerce Checkout Flow Reference
## Complete Checkout Event Sequence
```
view_cart → begin_checkout → add_shipping_info → add_payment_info → purchase
```
Each step must fire in order with consistent item data.
## Event Details
### 1. view_cart
When user views cart page.
```javascript
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: "view_cart",
ecommerce: {
currency: "KRW",
value: 125000,
items: [{
item_id: "SKU_001",
item_name: "Blue T-Shirt",
price: 45000,
quantity: 2,
item_brand: "Brand",
item_category: "Apparel"
}, {
item_id: "SKU_002",
item_name: "Black Jeans",
price: 35000,
quantity: 1
}]
}
});
```
### 2. begin_checkout
When user initiates checkout process.
```javascript
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: "begin_checkout",
ecommerce: {
currency: "KRW",
value: 125000,
coupon: "SUMMER10",
items: [/* same items as view_cart */]
}
});
```
### 3. add_shipping_info
When user completes shipping step.
```javascript
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: "add_shipping_info",
ecommerce: {
currency: "KRW",
value: 125000,
coupon: "SUMMER10",
shipping_tier: "Express", // Required
items: [/* same items */]
}
});
```
**shipping_tier values:**
- "Standard" / "일반배송"
- "Express" / "익일배송"
- "Same Day" / "당일배송"
- "Free" / "무료배송"
- "Store Pickup" / "매장픽업"
### 4. add_payment_info
When user enters payment details.
```javascript
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: "add_payment_info",
ecommerce: {
currency: "KRW",
value: 125000,
coupon: "SUMMER10",
payment_type: "Credit Card", // Required
items: [/* same items */]
}
});
```
**payment_type values:**
- "Credit Card" / "신용카드"
- "Debit Card" / "체크카드"
- "Bank Transfer" / "계좌이체"
- "Virtual Account" / "가상계좌"
- "Mobile Payment" / "휴대폰결제"
- "Kakao Pay" / "카카오페이"
- "Naver Pay" / "네이버페이"
- "Toss" / "토스"
- "PayPal"
### 5. purchase
When transaction completes successfully.
```javascript
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: "purchase",
ecommerce: {
transaction_id: "T_20250115_001234", // Required, unique
value: 130500, // Required (total)
tax: 11863,
shipping: 5000,
currency: "KRW", // Required
coupon: "SUMMER10",
items: [{
item_id: "SKU_001",
item_name: "Blue T-Shirt",
affiliation: "Online Store",
coupon: "SUMMER10",
discount: 4500,
price: 45000,
quantity: 2
}]
}
});
```
## Funnel Drop-off Analysis
### Tracking Drop-offs
Monitor completion rate at each step:
| Step | Event | Drop-off Indicator |
|------|-------|-------------------|
| Cart | view_cart | User leaves cart page |
| Checkout Start | begin_checkout | User doesn't proceed |
| Shipping | add_shipping_info | Address form abandoned |
| Payment | add_payment_info | Payment not completed |
| Complete | purchase | Transaction failed |
### Implementing Drop-off Tracking
```javascript
// Track checkout step viewed but not completed
let checkoutStep = 0;
function trackCheckoutProgress(step) {
if (step > checkoutStep) {
checkoutStep = step;
}
}
window.addEventListener('beforeunload', () => {
if (checkoutStep > 0 && checkoutStep < 5) {
dataLayer.push({
event: 'checkout_abandon',
last_step: checkoutStep,
step_name: ['cart', 'checkout', 'shipping', 'payment', 'complete'][checkoutStep - 1]
});
}
});
```
## Value Consistency Check
Ensure `value` matches across events:
```
view_cart.value = sum(items.price * items.quantity)
begin_checkout.value = view_cart.value
add_shipping_info.value = begin_checkout.value
add_payment_info.value = add_shipping_info.value
purchase.value = add_payment_info.value + shipping + tax - discount
```
## Common Issues
### Duplicate Purchase Events
**Problem**: Same order tracked multiple times
**Solution**:
```javascript
// Check if already tracked
const txId = "T_12345";
if (!sessionStorage.getItem('purchase_' + txId)) {
dataLayer.push({ event: 'purchase', ... });
sessionStorage.setItem('purchase_' + txId, 'true');
}
```
### Missing Items in Later Steps
**Problem**: Items present in view_cart but missing in purchase
**Solution**: Store cart data in session and reuse
### Inconsistent Currency
**Problem**: Some events use USD, others KRW
**Solution**: Standardize currency across all events
### Wrong Value Calculation
**Problem**: purchase.value doesn't include tax/shipping
**Solution**:
```
purchase.value = subtotal + tax + shipping - discount
```
## Korean E-commerce Platforms
### Cafe24
Custom dataLayer variable names - check documentation
### Shopify Korea
Standard GA4 format with `Shopify.checkout` object
### WooCommerce
Use official GA4 plugin or custom implementation
### Naver SmartStore
Separate Naver Analytics implementation required
## Checkout Flow Checklist
- [ ] view_cart fires on cart page load
- [ ] begin_checkout fires on checkout button click
- [ ] add_shipping_info includes shipping_tier
- [ ] add_payment_info includes payment_type
- [ ] purchase has unique transaction_id
- [ ] All events have consistent items array
- [ ] Currency is consistent across all events
- [ ] Value calculations are accurate
- [ ] ecommerce object cleared before each push
- [ ] Purchase event fires only once per order

View File

@@ -0,0 +1,211 @@
# Common GTM Issues & Fixes
## Container Issues
### GTM Not Firing
**Symptoms**: No GTM requests in network tab
**Causes**:
1. Script blocked by ad blocker
2. Script placed after closing body tag
3. JavaScript error before GTM loads
4. Consent management blocking GTM
**Fix**:
```html
<!-- Place immediately after opening <head> tag -->
<script>(function(w,d,s,l,i){...})(window,document,'script','dataLayer','GTM-XXXXXX');</script>
```
### Multiple Containers Conflict
**Symptoms**: Duplicate events, inconsistent data
**Causes**:
1. Legacy container not removed
2. Different teams installed separate containers
3. Theme/plugin auto-installed GTM
**Fix**:
1. Audit all containers in source
2. Consolidate to single container
3. Use GTM environments for staging/prod
### Container ID Mismatch
**Symptoms**: Tags not firing, wrong property receiving data
**Causes**:
1. Dev/staging container on production
2. Copy-paste error during installation
**Fix**: Verify container ID matches GTM account
---
## DataLayer Issues
### DataLayer Not Initialized
**Symptoms**: First push events lost
**Code Error**:
```javascript
// Wrong - GTM loads before dataLayer exists
<script>GTM snippet</script>
dataLayer.push({...});
```
**Fix**:
```javascript
// Correct - Initialize dataLayer first
<script>window.dataLayer = window.dataLayer || [];</script>
<script>GTM snippet</script>
```
### Case Sensitivity Issues
**Symptoms**: Triggers not matching
**Example**:
```javascript
// DataLayer pushes "AddToCart"
dataLayer.push({ event: "AddToCart" });
// But GTM trigger looks for "addToCart" - won't match!
```
**Fix**: Standardize event naming (recommend lowercase with underscores)
### Wrong Data Types
**Symptoms**: Calculations wrong in GA4, missing data
**Example**:
```javascript
// Wrong - price as string
dataLayer.push({ ecommerce: { value: "29.99" }});
// Correct - price as number
dataLayer.push({ ecommerce: { value: 29.99 }});
```
### Timing Issues
**Symptoms**: Events fire before data available
**Cause**: DataLayer push happens after tag fires
**Fix**: Use "Custom Event" trigger instead of "Page View"
---
## Tag Issues
### Tag Not Firing
**Checklist**:
1. ✓ Trigger conditions met?
2. ✓ Trigger enabled?
3. ✓ Tag not paused?
4. ✓ No blocking triggers active?
5. ✓ Consent mode not blocking?
**Debug Steps**:
1. GTM Preview > Check Tags Fired
2. Verify trigger shows green check
3. Check Variables tab for expected values
### Duplicate Tag Firing
**Symptoms**: Events counted 2x in GA4
**Causes**:
1. Multiple triggers on same action
2. Page re-renders triggering again
3. SPA virtual pageviews firing multiple times
**Fix**:
1. Add "Once per event" tag firing option
2. Use trigger groups to control firing
3. Add conditions to prevent re-firing
### Wrong Parameters Sent
**Symptoms**: Data appears in wrong fields in GA4
**Debug**:
1. GTM Preview > Tags > Show fired tag
2. Check "Values" sent with tag
3. Compare with expected parameters
---
## E-commerce Issues
### Missing Transaction ID
**Symptoms**: Duplicate purchases counted
**Fix**: Ensure unique `transaction_id` generated server-side
### Items Array Empty
**Symptoms**: Revenue tracked but no products
**Check**: `ecommerce.items` array populated
### Value Mismatch
**Symptoms**: Revenue doesn't match actual
**Causes**:
1. Tax/shipping included inconsistently
2. Currency conversion issues
3. Discount applied incorrectly
### Purchase Event Fires Multiple Times
**Symptoms**: Same order tracked 2-3x
**Causes**:
1. Page refresh on confirmation
2. Browser back button
3. Email link revisit
**Fix**:
```javascript
// Check if already tracked
if (!sessionStorage.getItem('purchase_' + transaction_id)) {
dataLayer.push({ event: 'purchase', ... });
sessionStorage.setItem('purchase_' + transaction_id, 'true');
}
```
---
## Consent Mode Issues
### Tags Blocked by Consent
**Symptoms**: Tags show "Blocked by consent" in Preview
**Fix**:
1. Verify consent mode implementation
2. Check default consent state
3. Test with consent granted
### Consent Not Updating
**Symptoms**: Tags stay blocked after user accepts
**Fix**: Verify `gtag('consent', 'update', {...})` fires on accept
---
## SPA (Single Page App) Issues
### Pageviews Not Tracking Navigation
**Symptoms**: Only initial pageview tracked
**Cause**: No page reload on route change
**Fix**: Implement History Change trigger or custom event:
```javascript
// On route change
dataLayer.push({
event: 'virtual_pageview',
page_path: newPath,
page_title: newTitle
});
```
### Events Fire on Old Page Data
**Symptoms**: Wrong page_path in events
**Fix**: Update page variables before event push
---
## Performance Issues
### Tags Slowing Page Load
**Symptoms**: High LCP, slow TTI
**Causes**:
1. Too many synchronous tags
2. Large third-party scripts
3. Tags in wrong firing sequence
**Fix**:
1. Use tag sequencing
2. Load non-critical tags on Window Loaded
3. Defer marketing tags

View File

@@ -0,0 +1,287 @@
# DataLayer Validation Reference
## DataLayer Structure Basics
### Proper Initialization
```javascript
// Must appear BEFORE GTM script
<script>
window.dataLayer = window.dataLayer || [];
</script>
<!-- GTM script here -->
```
### Push Syntax
```javascript
// Correct
dataLayer.push({ event: "page_view", page_title: "Home" });
// Wrong - direct assignment
dataLayer = [{ event: "page_view" }]; // ❌ Overwrites array
```
## Validation Rules
### Event Names
| Rule | Valid | Invalid |
|------|-------|---------|
| Alphanumeric + underscore | `add_to_cart` | `add-to-cart` |
| Max 40 characters | `purchase` | (too long names) |
| Case sensitive | `addToCart``addtocart` | - |
| No spaces | `form_submit` | `form submit` |
| No special chars | `click_cta` | `click@cta` |
### Parameter Names
| Rule | Valid | Invalid |
|------|-------|---------|
| Max 40 characters | `item_category` | (too long) |
| Alphanumeric + underscore | `user_id` | `user-id` |
| Cannot start with `_` | `custom_param` | `_private` |
| Cannot start with number | `step_1` | `1_step` |
### Data Types
| Parameter | Expected Type | Example |
|-----------|---------------|---------|
| value | number | `29.99` not `"29.99"` |
| currency | string (ISO 4217) | `"USD"`, `"KRW"` |
| transaction_id | string | `"T_12345"` |
| quantity | integer | `2` not `2.0` |
| price | number | `45000` |
| items | array | `[{...}, {...}]` |
### Type Validation Code
```javascript
function validateDataLayerPush(data) {
const issues = [];
// Check value is number
if (data.ecommerce?.value !== undefined) {
if (typeof data.ecommerce.value !== 'number') {
issues.push(`value should be number, got ${typeof data.ecommerce.value}`);
}
}
// Check currency format
if (data.ecommerce?.currency) {
if (!/^[A-Z]{3}$/.test(data.ecommerce.currency)) {
issues.push(`currency should be 3-letter ISO code`);
}
}
// Check items array
if (data.ecommerce?.items) {
if (!Array.isArray(data.ecommerce.items)) {
issues.push(`items should be array`);
} else {
data.ecommerce.items.forEach((item, i) => {
if (!item.item_id) issues.push(`items[${i}] missing item_id`);
if (!item.item_name) issues.push(`items[${i}] missing item_name`);
if (item.price && typeof item.price !== 'number') {
issues.push(`items[${i}].price should be number`);
}
if (item.quantity && !Number.isInteger(item.quantity)) {
issues.push(`items[${i}].quantity should be integer`);
}
});
}
}
return issues;
}
```
## E-commerce Object Clearing
### Why Clear?
GA4 may merge previous ecommerce data with new events.
### Correct Pattern
```javascript
// Clear first
dataLayer.push({ ecommerce: null });
// Then push new event
dataLayer.push({
event: "view_item",
ecommerce: { ... }
});
```
### Validation Check
```javascript
function checkEcommerceClear(dataLayerArray) {
let lastHadEcommerce = false;
const issues = [];
dataLayerArray.forEach((item, i) => {
const hasEcommerce = 'ecommerce' in item;
const isNull = item.ecommerce === null;
if (hasEcommerce && !isNull && lastHadEcommerce) {
issues.push({
index: i,
message: 'Missing ecommerce:null before this push'
});
}
lastHadEcommerce = hasEcommerce && !isNull;
});
return issues;
}
```
## Event Sequence Validation
### Expected Sequences
**E-commerce Purchase Flow:**
```
view_item_list? → view_item → add_to_cart → view_cart →
begin_checkout → add_shipping_info → add_payment_info → purchase
```
**Form Submission:**
```
form_start → form_submit → generate_lead?
```
**User Authentication:**
```
login | sign_up
```
### Sequence Validator
```javascript
function validateSequence(events, expectedOrder) {
const eventNames = events
.filter(e => e.event)
.map(e => e.event);
let lastIndex = -1;
const issues = [];
eventNames.forEach(event => {
const index = expectedOrder.indexOf(event);
if (index !== -1) {
if (index < lastIndex) {
issues.push(`${event} fired out of expected order`);
}
lastIndex = index;
}
});
return issues;
}
```
## Duplicate Event Detection
### Common Duplicates
- Multiple `page_view` on single page load
- `purchase` firing on page refresh
- Click events on bubbling elements
### Detection Code
```javascript
function findDuplicates(events) {
const seen = {};
const duplicates = [];
events.forEach((event, i) => {
if (!event.event) return;
const key = JSON.stringify(event);
if (seen[key]) {
duplicates.push({
event: event.event,
firstIndex: seen[key],
duplicateIndex: i
});
} else {
seen[key] = i;
}
});
return duplicates;
}
```
## Real-time Monitoring Setup
### Console Monitoring
```javascript
// Paste in browser console to monitor pushes
(function() {
const original = dataLayer.push;
dataLayer.push = function() {
console.group('📊 dataLayer.push');
console.log('Data:', arguments[0]);
console.log('Time:', new Date().toISOString());
console.groupEnd();
return original.apply(this, arguments);
};
console.log('✅ DataLayer monitoring active');
})();
```
### Export DataLayer
```javascript
// Copy full dataLayer to clipboard
copy(JSON.stringify(dataLayer, null, 2));
```
## Validation Checklist
### Structure
- [ ] dataLayer initialized before GTM
- [ ] Using push() not assignment
- [ ] Event names follow conventions
- [ ] Parameter names follow conventions
### Data Types
- [ ] value is number
- [ ] currency is 3-letter code
- [ ] quantity is integer
- [ ] items is array
- [ ] Required fields present
### E-commerce
- [ ] ecommerce:null before each push
- [ ] items array has item_id and item_name
- [ ] transaction_id is unique
- [ ] Consistent currency across events
### Sequence
- [ ] Events fire in logical order
- [ ] No duplicate events
- [ ] Purchase fires only once
## Debug Tools
### GTM Preview Mode
- Real-time event inspection
- Variable value checking
- Tag firing verification
### GA4 DebugView
- Live event stream
- Parameter validation
- User property tracking
### Browser Console
```javascript
// View current dataLayer
console.table(dataLayer);
// Filter by event
dataLayer.filter(d => d.event === 'purchase');
```

View File

@@ -0,0 +1,216 @@
# E-commerce DataLayer Schema Reference
## GA4 E-commerce Structure
### Items Array Schema
Every e-commerce event requires an `items` array:
```javascript
items: [{
// Required
item_id: "SKU_12345",
item_name: "Blue T-Shirt",
// Recommended
affiliation: "Store Name",
coupon: "SUMMER_SALE",
discount: 5.00,
index: 0,
item_brand: "Brand Name",
item_category: "Apparel",
item_category2: "Men",
item_category3: "Shirts",
item_category4: "T-Shirts",
item_category5: "Short Sleeve",
item_list_id: "related_products",
item_list_name: "Related Products",
item_variant: "Blue/Large",
location_id: "ChIJIQBpAG2ahYAR_6128GcTUEo",
price: 29.99,
quantity: 1
}]
```
### Clear Previous E-commerce Data
Always clear before new e-commerce event:
```javascript
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: "view_item",
ecommerce: {
// new data
}
});
```
## Complete Purchase Flow
### 1. Product List View
```javascript
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: "view_item_list",
ecommerce: {
item_list_id: "category_results",
item_list_name: "Category Results",
items: [
{ item_id: "SKU_001", item_name: "Product 1", index: 0, price: 29.99 },
{ item_id: "SKU_002", item_name: "Product 2", index: 1, price: 39.99 }
]
}
});
```
### 2. Product Click
```javascript
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: "select_item",
ecommerce: {
item_list_id: "category_results",
item_list_name: "Category Results",
items: [{
item_id: "SKU_001",
item_name: "Product 1",
price: 29.99
}]
}
});
```
### 3. Product Detail View
```javascript
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: "view_item",
ecommerce: {
currency: "USD",
value: 29.99,
items: [{
item_id: "SKU_001",
item_name: "Product 1",
item_brand: "Brand",
item_category: "Category",
price: 29.99,
quantity: 1
}]
}
});
```
### 4. Add to Cart
```javascript
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: "add_to_cart",
ecommerce: {
currency: "USD",
value: 29.99,
items: [{
item_id: "SKU_001",
item_name: "Product 1",
price: 29.99,
quantity: 1
}]
}
});
```
### 5. View Cart
```javascript
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: "view_cart",
ecommerce: {
currency: "USD",
value: 59.98,
items: [
{ item_id: "SKU_001", item_name: "Product 1", price: 29.99, quantity: 1 },
{ item_id: "SKU_002", item_name: "Product 2", price: 29.99, quantity: 1 }
]
}
});
```
### 6. Begin Checkout
```javascript
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: "begin_checkout",
ecommerce: {
currency: "USD",
value: 59.98,
coupon: "DISCOUNT10",
items: [...]
}
});
```
### 7. Add Shipping Info
```javascript
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: "add_shipping_info",
ecommerce: {
currency: "USD",
value: 59.98,
shipping_tier: "Standard",
items: [...]
}
});
```
### 8. Add Payment Info
```javascript
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: "add_payment_info",
ecommerce: {
currency: "USD",
value: 59.98,
payment_type: "Credit Card",
items: [...]
}
});
```
### 9. Purchase
```javascript
dataLayer.push({ ecommerce: null });
dataLayer.push({
event: "purchase",
ecommerce: {
transaction_id: "T_12345",
value: 65.97,
tax: 4.99,
shipping: 5.99,
currency: "USD",
coupon: "DISCOUNT10",
items: [{
item_id: "SKU_001",
item_name: "Product 1",
affiliation: "Online Store",
coupon: "DISCOUNT10",
discount: 3.00,
item_brand: "Brand",
item_category: "Category",
price: 29.99,
quantity: 1
}]
}
});
```
## Korean E-commerce Considerations
### Currency
```javascript
currency: "KRW",
value: 35000 // No decimals for KRW
```
### Common Korean Platform Integrations
- Cafe24: Uses custom dataLayer structure
- Shopify Korea: Standard GA4 format
- Naver SmartStore: Custom pixel implementation

View File

@@ -0,0 +1,157 @@
# Form Tracking Reference
## GA4 Form Events
### form_start
Fires on first interaction with form field.
```javascript
dataLayer.push({
event: "form_start",
form_id: "contact-form",
form_name: "Contact Us",
form_destination: "/submit-contact"
});
```
### form_submit
Fires on successful form submission.
```javascript
dataLayer.push({
event: "form_submit",
form_id: "contact-form",
form_name: "Contact Us",
form_destination: "/submit-contact",
form_submit_text: "Send Message"
});
```
### generate_lead
Fires when form generates a qualified lead.
```javascript
dataLayer.push({
event: "generate_lead",
currency: "USD",
value: 100, // Estimated lead value
form_id: "quote-request"
});
```
## Form Field Events (Custom)
### field_focus
```javascript
dataLayer.push({
event: "field_focus",
form_id: "signup-form",
field_name: "email",
field_type: "email"
});
```
### field_complete
```javascript
dataLayer.push({
event: "field_complete",
form_id: "signup-form",
field_name: "email",
field_type: "email",
is_valid: true
});
```
### field_error
```javascript
dataLayer.push({
event: "field_error",
form_id: "signup-form",
field_name: "email",
error_message: "Invalid email format"
});
```
## Form Abandonment Tracking
### Detecting Abandonment
Track when user leaves form without submitting:
```javascript
// Track form start
let formStarted = false;
document.querySelectorAll('form input, form select, form textarea')
.forEach(field => {
field.addEventListener('focus', function() {
if (!formStarted) {
formStarted = true;
dataLayer.push({ event: 'form_start', form_id: this.form.id });
}
});
});
// Track abandonment on page leave
window.addEventListener('beforeunload', function() {
if (formStarted && !formSubmitted) {
dataLayer.push({
event: 'form_abandon',
form_id: 'contact-form',
last_field: lastFocusedField,
fields_completed: completedFieldCount
});
}
});
```
## GTM Trigger Configuration
### Form Submission Trigger
| Setting | Value |
|---------|-------|
| Trigger Type | Form Submission |
| Wait for Tags | Check (if AJAX form) |
| Check Validation | Check |
| Form ID | equals `contact-form` |
### Form Start Trigger (Custom Event)
| Setting | Value |
|---------|-------|
| Trigger Type | Custom Event |
| Event Name | form_start |
| Fire On | All Custom Events |
## Common Form Types & Tracking
### Contact Forms
Events: `form_start`, `form_submit`, `generate_lead`
### Newsletter Signup
Events: `form_start`, `form_submit`, `sign_up`
### Login Forms
Events: `form_start`, `login`
### Search Forms
Events: `search` (with search_term parameter)
### Multi-Step Forms
Track each step:
```javascript
dataLayer.push({
event: "form_step",
form_id: "checkout-form",
step_number: 2,
step_name: "Shipping Address"
});
```
## Validation Checklist
- [ ] Form has id or name attribute
- [ ] All required fields have names
- [ ] Submit button identifiable
- [ ] form_start fires on first interaction
- [ ] form_submit fires only on success
- [ ] generate_lead has value parameter
- [ ] Error events track validation failures
- [ ] Abandonment tracking implemented (optional)

View File

@@ -0,0 +1,177 @@
# GA4 Recommended Events Reference
## Automatically Collected Events
Events GA4 collects without configuration:
- `first_visit` - First time user visits
- `session_start` - Session begins
- `page_view` - Page loads (enhanced measurement)
- `scroll` - 90% scroll depth
- `click` - Outbound link clicks
- `file_download` - File download clicks
- `video_start`, `video_progress`, `video_complete` - YouTube embeds
## E-commerce Events (Required Parameters)
### view_item_list
```javascript
{
event: "view_item_list",
ecommerce: {
item_list_id: "related_products",
item_list_name: "Related Products",
items: [{
item_id: "SKU_12345", // required
item_name: "Product Name", // required
price: 29.99,
quantity: 1
}]
}
}
```
### view_item
```javascript
{
event: "view_item",
ecommerce: {
currency: "USD",
value: 29.99,
items: [{
item_id: "SKU_12345", // required
item_name: "Product Name", // required
price: 29.99,
quantity: 1
}]
}
}
```
### add_to_cart
```javascript
{
event: "add_to_cart",
ecommerce: {
currency: "USD",
value: 29.99,
items: [{
item_id: "SKU_12345", // required
item_name: "Product Name", // required
price: 29.99,
quantity: 1
}]
}
}
```
### begin_checkout
```javascript
{
event: "begin_checkout",
ecommerce: {
currency: "USD",
value: 99.99,
coupon: "SUMMER_SALE",
items: [...]
}
}
```
### add_payment_info
```javascript
{
event: "add_payment_info",
ecommerce: {
currency: "USD",
value: 99.99,
payment_type: "credit_card",
items: [...]
}
}
```
### purchase
```javascript
{
event: "purchase",
ecommerce: {
transaction_id: "T12345", // required, must be unique
value: 99.99, // required
currency: "USD", // required
tax: 4.99,
shipping: 5.99,
coupon: "SUMMER_SALE",
items: [{
item_id: "SKU_12345", // required
item_name: "Product Name",// required
price: 29.99,
quantity: 2
}]
}
}
```
## Lead Generation Events
### generate_lead
```javascript
{
event: "generate_lead",
currency: "USD",
value: 100 // estimated lead value
}
```
### sign_up
```javascript
{
event: "sign_up",
method: "email" // or "google", "facebook", etc.
}
```
### login
```javascript
{
event: "login",
method: "email"
}
```
## Engagement Events
### search
```javascript
{
event: "search",
search_term: "blue shoes"
}
```
### share
```javascript
{
event: "share",
method: "twitter",
content_type: "article",
item_id: "article_123"
}
```
## Parameter Validation Rules
| Parameter | Type | Max Length | Notes |
|-----------|------|------------|-------|
| event name | string | 40 chars | No spaces, alphanumeric + underscore |
| item_id | string | 100 chars | Required for e-commerce |
| item_name | string | 100 chars | Required for e-commerce |
| currency | string | 3 chars | ISO 4217 format (USD, KRW, etc.) |
| transaction_id | string | 100 chars | Must be unique per transaction |
| value | number | - | Numeric, no currency symbols |
## Common Validation Errors
1. **Missing required params**: `item_id` or `item_name` not in items array
2. **Wrong data type**: `value` as string instead of number
3. **Duplicate transaction_id**: Same ID used for multiple purchases
4. **Empty items array**: E-commerce event with no items
5. **Invalid currency**: Currency code not in ISO 4217 format

View File

@@ -0,0 +1,115 @@
# GTM Audit Report Template
## Executive Summary
| Metric | Status |
|--------|--------|
| Container Installed | ✅ / ❌ |
| Container Valid | ✅ / ❌ |
| DataLayer Active | ✅ / ❌ |
| Tags Firing | X of Y |
| Critical Issues | X |
| Warnings | X |
## Container Status
**Container ID**: GTM-XXXXXX
**Installation Position**: head / body
**Multiple Containers**: Yes / No
**Noscript Fallback**: Present / Missing
### Issues Found
- [ ] Issue description
## DataLayer Analysis
### Events Captured
| Event Name | Count | Has Issues |
|------------|-------|------------|
| page_view | 1 | No |
| add_to_cart | 0 | - |
### DataLayer Quality
- [ ] Initialized before GTM
- [ ] Standard event naming
- [ ] Correct data types
- [ ] E-commerce structure valid
## Tag Firing Report
### Tags Fired ✅
| Destination | Events | Parameters |
|-------------|--------|------------|
| GA4 | page_view | page_location, page_title |
| Meta Pixel | PageView | - |
### Tags Not Detected ⚠️
| Expected Tag | Reason | Priority |
|--------------|--------|----------|
| GA4 purchase | Event not triggered | High |
## Network Request Analysis
Total requests captured: X
### By Destination
| Destination | Requests | Status |
|-------------|----------|--------|
| GA4 | X | ✅ |
| Meta | X | ✅ |
| Google Ads | 0 | ⚠️ |
## Issues & Recommendations
### Critical 🔴
1. **Issue Title**
- Description
- Impact
- Recommended Fix
### Warning 🟡
1. **Issue Title**
- Description
- Recommended Fix
### Info 🔵
1. **Issue Title**
- Description
## Action Items Checklist
### Immediate (Critical)
- [ ] Action item 1
- [ ] Action item 2
### Short-term (This Week)
- [ ] Action item 3
### Long-term (This Month)
- [ ] Action item 4
## Technical Details
### Environment
- URL Audited: https://example.com
- Audit Timestamp: YYYY-MM-DD HH:MM:SS
- Browser: Chromium (headless)
- Viewport: 1920x1080
### Raw Data
Full JSON report available at: `gtm_audit_report.json`
---
## Appendix: Tag Destination Reference
| Tag Type | Network Pattern |
|----------|-----------------|
| GA4 | google-analytics.com/g/collect |
| UA (Legacy) | google-analytics.com/collect |
| Google Ads | googleads.g.doubleclick.net |
| Meta Pixel | facebook.com/tr |
| LinkedIn | px.ads.linkedin.com |
| TikTok | analytics.tiktok.com |
| Kakao | pixel.kakao.com |
| Naver | wcs.naver.com |