💡 카페24 자사몰과 GA4를 연결할 때, 가장 먼저 해야 할 일은 ‘전자상거래’ 분석 세팅입니다. 단순히 GA4만 연결해 뒀다면 자사몰에 중요한 구매(purchase) 등 정보는 추적되지 않습니다. 이번 포스팅에서는 GTM을 이용해서 카페24에서 GA4 전자상거래 이벤트를 추적하는 방법을 알아보겠습니다. (네이버 페이, 카카오 페이 대응)
1. 개요
1) 전자상거래 이벤트란?
GA4는 사용자의 모든 행동을 ‘이벤트’로 분류해서 추적합니다. GA4를 연결만 해도 유입, 페이지 조회 등 기본 이벤트는 자동으로 추적되지만, 자사몰에서 중요한 장바구니, 구매 등 이벤트는 별도로 세팅이 필요합니다. 이러한 이벤트를 ‘전자상거래’ 이벤트라고 부릅니다. 주요 전자상거래 이벤트는 아래 표를 참고하세요.
| 전자상거래 이벤트 | 설명 |
|---|---|
| view_item | 상품 조회 |
| add_to_cart | 장바구니 |
| begin_checkout | 주문서 작성 |
| purchase | 구매 |
아임웹 등 일부 플랫폼에서는 API를 통해 홈페이지 관리자에서 편리하게 GA4 전자상거래 연동을 할 수 있습니다. 다만, 국내에서 가장 점유율이 높은 카페24 같은 경우 GA4 전자상거래 연동 옵션이 없어서 GTM이나 기타 솔루션을 사용해야 합니다.
이번 포스팅에서는 누구나 따라할 수 있도록 GTM을 사용한 방법을 안내합니다.
2) GTM과 GA4 사용법
GTM과 GA4의 기본적인 설치, 사용법은 아래 포스팅을 참고해 주세요.
참고로 카페24에서는 쇼핑몰 설정 → 기본 설정 → 검색엔진 최적화(SEO) → 코드 직접입력 메뉴에서 Head와 Body에 GTM 스니펫을 입력할 수 있습니다.
2. GTM 세팅
1) 변수 입력
아래 항목을 모두 사용자 정의 변수에 입력해 주세요.
a. 맞춤 자바스크립트 변수
자바스크립트 변수는 스크립트 추가가 필요합니다. 아래 이미지를 참고하여 변수명 – 변수 유형 – 스크립트를 작성해 주세요.

변수명: 01productInfo
변수 유형: 맞춤 자바스크립트
스크립트:
function() {
var productInfo = [{
'item_id': iProductNo,
'item_name': product_name,
'price': product_price,
'item_category': iCategoryNo
}];
return productInfo;
}
변수명: 02orderInfo
변수 유형: 맞춤 자바스크립트
스크립트:
function() {
var source = aBasketProductOrderData;
var productInfo = [];
for(var i = 0 ; i < source.length ; i++) {
var productId = source[i].product_no;
var productPrice = source[i].product_sale_price;
var productQty = source[i].quantity;
var productCate = source[i].main_cate_no;
productInfo.push({
'item_id': productId,
'price': productPrice,
'quantity': productQty,
'item_category': productCate
});
}
return productInfo;
}
변수명: 03orderId
변수 유형: 맞춤 자바스크립트
스크립트:
function() {
var source = EC_FRONT_EXTERNAL_SCRIPT_VARIABLE_DATA;
var orderId = source.order_id;
return orderId;
}
변수명: 04orderPrice
변수 유형: 맞춤 자바스크립트
스크립트:
function() {
var source = EC_FRONT_EXTERNAL_SCRIPT_VARIABLE_DATA;
var revenue = source.payed_amount;
return revenue;
}
변수명: 05shipFee
변수 유형: 맞춤 자바스크립트
스크립트:
function() {
var source = EC_FRONT_EXTERNAL_SCRIPT_VARIABLE_DATA;
var shippingFee = source.total_basic_ship_fee;
return shippingFee;
}
변수명: 06orderProductInfo
변수 유형: 맞춤 자바스크립트
스크립트:
function() {
var source = EC_FRONT_EXTERNAL_SCRIPT_VARIABLE_DATA.order_product;
var productInfo = [];
for(var i = 0 ; i < source.length ; i++) {
var productId = source[i].product_no;
var productName = source[i].product_name;
var productPrice = source[i].product_price;
var productQty = source[i].quantity;
var productCate = source[i].category_no_2;
productInfo.push({
'item_id': productId,
'item_name': productName,
'price': productPrice,
'item_category': productCate,
'quantity': productQty
});
}
return productInfo;
}
변수명: 07User_ID
변수 유형: 맞춤 자바스크립트
스크립트:
function(){
var uid = EC_FRONT_EXTERNAL_SCRIPT_VARIABLE_DATA.common_member_id_crypt;
return uid;
}
변수명: pushDataLayerForEcommerce
변수 유형: 맞춤 자바스크립트
스크립트:
function() {
var npay_trans_id = '';
var times = {{time_string}};
return function( act, productInfo, listName, dataLayer ) {
switch( act ) {
/* GA4 */
case 'nPay_checkoutCartGA4':
case 'nPay_checkoutGA4' :
var total_cnt = productInfo.length;
var total_price =0;
for(var i=0;i<total_cnt;i++) {
total_price += (parseInt(productInfo[i].price) * parseInt(productInfo[i].quantity));
}
var tPrice = $('.totalPrice .total em').text();
if ( tPrice ) total_price = tPrice.replace(/[^d]/g, '');
npay_trans_id = times+'-npay'+listName.orderId // 여기서는 주문번호입니다.
var orderInfo = {'transaction_id':npay_trans_id,'value':total_price, 'shipping':0, 'currency':'KRW', 'payment_type':'npay'};
orderInfo.items = productInfo;
if ( total_cnt > 0 ) {
var product_name = productInfo[0].item_name;
if ( total_cnt > 1 ) {
product_name = product_name + '외 ' + ( total_cnt - 1);
}
}
dataLayer.push({ 'ecommerce': null });
dataLayer.push({'event':'nPaypurchaseG4', 'product_title':product_name, 'ecommerce': orderInfo});
break;
case 'kakao_checkoutGA4' :
case 'kakao_checkoutCartGA4':
var total_cnt = productInfo.length;
var total_price =0;
for(var i=0;i<total_cnt;i++) {
total_price += (parseInt(productInfo[i].price) * parseInt(productInfo[i].quantity));
}
var tPrice = $('.totalPrice.kd-clear .total em').text();
if ( tPrice ) total_price = tPrice.replace(/[^d]/g, '');
kakao_trans_id = times+'-kakao'+ listName.orderId ;// 여기서는 주문번호입니다.
var orderInfo = {'transaction_id':kakao_trans_id,'value':total_price, 'shipping':0, 'currency':'KRW', 'payment_type':'kpay'};
orderInfo.items = productInfo;
if ( total_cnt > 0 ) {
var product_name = productInfo[0].name;
if ( total_cnt > 1 ) {
product_name = product_name + '외 ' + ( total_cnt - 1);
}
}
dataLayer.push({ ecommerce: null });
dataLayer.push({'event':'kPaypurchaseG4', 'product_title':product_name, 'ecommerce': orderInfo});
break;
}
}
}
변수명: actionEcommerce
변수 유형: 맞춤 자바스크립트
스크립트:
function() {
var pushDataLayer = {{pushDataLayerForEcommerce}};
return function(event, act, dataLayer, productData ) {
switch(act) {
/* GA4 용 */
case 'nPay_checkoutGA4' :
case 'kakao_checkoutGA4' :
var getProductInfo = {{getProductInfoFromOptionForGA4}};
var productInfo = getProductInfo();
if( productInfo.length ) pushDataLayer(act, productInfo, productData, dataLayer);
break;
case 'nPay_checkoutCartGA4':
case 'kakao_checkoutCartGA4':
var getProductInfo = {{getProductInfoFromBasketForGA4}};
var productInfo = getProductInfo(productData);
if( productInfo.length ) pushDataLayer(act, productInfo, productData, dataLayer);
break;
}
};
}
변수명: time_string
변수 유형: 맞춤 자바스크립트
스크립트:
function() {
var today = new Date();
var year = today.getFullYear();
var month = ('0' + (today.getMonth() + 1)).slice(-2);
var day = ('0' + today.getDate()).slice(-2);
var hours = ('0' + today.getHours()).slice(-2);
var minutes = ('0' + today.getMinutes()).slice(-2);
var seconds = ('0' + today.getSeconds()).slice(-2);
var timeString = year + month + day + hours + minutes + seconds;
return timeString;
}
변수명: getProductInfoFromBasketForGA4
변수 유형: 맞춤 자바스크립트
스크립트:
function() {
return function( position ) {
var productInfo = [];
var getProductVariant = {{getProductVariant}};
if (typeof position !== 'undefined') {
var localPosition = position.hasOwnProperty('position') ? position.position : position;
var loop = localPosition.length;
for( var i =0; i < loop ; i++ ) {
var loop_position = localPosition[ i ];
var variant = getProductVariant(aBasketProductData[loop_position].option_str);
productInfo.push({
'item_id': aBasketProductData[loop_position].product_no,
'item_name': aBasketProductData[loop_position].product_name,
'item_category': aBasketProductData[loop_position].main_cate_no,
'item_variant' : variant,
'price': aBasketProductData[loop_position].product_price,
'quantity': aBasketProductData[loop_position].quantity,
});
}
}
return productInfo;
};
}
변수명: getProductInfoFromOptionForGA4
변수 유형: 맞춤 자바스크립트
스크립트:
function() {
return function() {
var productInfo = [];
var isCheck = false;
var isSoldOut = false;
if ( typeof oProductList != 'undefined' ) {
if ( typeof option_stock_data != 'undefined' ) {
var products = JSON.parse(option_stock_data);
if ( typeof add_option_data != 'undefined' ) {
var addedProduct = JSON.parse(add_option_data);
for( idx in addedProduct ) {
if( addedProduct[idx].item_count > 1 ) {
var items = JSON.parse( addedProduct[idx].option_stock_data ) ;
for( i in items ) {
items[ i ].product_no = idx; items[ i ].product_name = addedProduct[idx].product_name;
}
products = Object.assign(products, items );
} else {
var items = '{"'+addedProduct[idx].item_code+'" : {"option_price" :'+addedProduct[idx].product_price+', "option_value_orginal": [], "product_no": "'+idx+'","product_name": "'+addedProduct[idx].product_name+'", "is_auto_soldout":"'+addedProduct[idx].is_auto_soldout+'" }}';
products = Object.assign(products, JSON.parse(items) );
}
}
}
for(idx in oProductList){
var quantity = oProductList[idx];
var price = products[idx].option_price;
var options = products[idx].option_value_orginal;
var variant = '';
options.forEach(function(val){ variant += (','+ val); })
variant = variant.substr(1);
var productNo = '', productName = '';
if (products[idx].hasOwnProperty('product_no') ) {
productNo = products[idx].product_no;
productName = products[idx].product_name;
} else {
productNo = iProductNo;
productName = product_name;
if (!isCheck ) isCheck = true;
}
if ( products[idx].is_auto_soldout == 'T' && products[idx].stock_number == 0 ) {
if( !isSoldOut ) isSoldOut = true;
}
productInfo.push({
'item_id': productNo,
'item_name': productName,
'item_category': iCategoryNo,
'item_variant' : variant,
'price': price,
'quantity': quantity,
});
};
if( isSoldOut ) productInfo = [];
} else {
var quantity = 0;
for(idx in oProductList) {
quantity = oProductList[idx];
}
if (!isCheck ) isCheck = true;
productInfo.push({
'item_id': iProductNo,
'item_name': product_name,
'item_category': iCategoryNo,
'price': product_price,
'quantity': quantity,
});
}
}
if (isCheck) return productInfo;
else return [];
}
}
변수명: getProductVariant
변수 유형: 맞춤 스크립트
스크립트:
function() {
return function( option_str ) {
var rtn = '';
if ( Array.isArray(option_str) ) {
rtn = option_str[0].replace(/^[\[]옵션:(.+)\]$/, '$1').trim().replace(/[\/]/g, ',');
} else {
if ( option_str ) rtn = option_str.replace(/^[\[]옵션:(.+)\]$/, '$1').trim().replace(/[\/]/g, ',');
}
return rtn;
};
}
변수명: getPositionFromBasket
변수 유형: 맞춤 자바스크립트
스크립트:
function() {
return function( posAll ) {
posAll = (typeof posAll !== 'undefined') ? posAll : true;
var position = [];
var productInfo = null;
if ( mobileWeb ) {
productInfo = $('.ec-base-prdInfo.xans-record-:has(input[id^=basket_chk_id])');
} else {
productInfo = $('tr.xans-record-:has(input[id^=basket_chk_id])');
if ( !productInfo.length ) productInfo = $('.prdList .item.xans-record-');
}
var len = productInfo.length;
for(i=0;i<len;i++) {
if ( posAll ) {
position.push(i);
} else {
var temp =$(productInfo[i]).find('input[id^=basket_chk_id]:checked').length;
if ( temp ) position.push(i);
}
}
return position;
};
}
b. 데이터 영역 변수
데이터 영역 변수는 아래 이미지를 참고하여 하나씩 추가해 주세요.

| 변수명 | 변수 유형 | 데이터 영역 변수 이름 |
|---|---|---|
| Ecommerce Currency | 데이터 영역 변수 | ecommerce.currency |
| Ecommerce Items | 데이터 영역 변수 | ecommerce.items |
| Ecommerce Payment Type | 데이터 영역 변수 | ecommerce.payment_type |
| Ecommerce Shipping | 데이터 영역 변수 | ecommerce.shipping |
| Ecommerce Transaction ID | 데이터 영역 변수 | ecommerce.transaction_id |
| Ecommerce Value | 데이터 영역 변수 | ecommerce.value |
2) 트리거 입력
주요 전자상거래 이벤트인 view_item, add_to_cart, begin_checkout, purchase에 대한 트리거 세팅입니다. 네이버 페이, 카카오 페이도 대응됩니다.
각 사이트 구조에 따라 실행 조건이 달라질 수 있습니다. 아래 이미지를 참고하여 트리거 항목에 하나씩 추가해 주세요.
a. 맞춤 이벤트 트리거

| 트리거 이름 | 이벤트 이름 | 비고 |
|---|---|---|
| 06view_item | view_item | |
| 07add_to_cart | add_to_cart | |
| 08begin_checkout | begin_checkout | |
| 09purchase | purchase | |
| 13npay_purchase | ^(nPaypurchaseG4|kPaypurchaseG4)$ | 체크: 일치하는 정규 표현식 사용 |
b. 그외 이벤트 트리거

| 트리거 이름 | 트리거 유형 | 필터 | 비고 |
|---|---|---|---|
| 02viewitem | 페이지뷰 – DOM 사용 가능 | Page Path 정규 표현식과 일치 | ^(?=.*product)(?=.*category)(?=.*display).* |
| 03actionCart | 클릭 – 모든 요소 | Click Text 포함 | 장바구니 |
| 04orderform | 페이지뷰 – DOM 사용 가능 | Page Path 포함 | /order/orderform.html |
| 05orderresult | 페이지뷰 – DOM 사용 가능 | Page Path 포함 | /order/order_result.html |
| 10dom+purchase | 트리거 그룹 | [05orderresult] and [09purchase] | (2개 트리거 결합) |
| 11view_detail | 페이지뷰 – DOM 사용 가능 | Page Path 정규 표현식과 일치 | ^(\/product\/[^\/]+\/[\d]+\/|\/product\/detail.html|\/product\/basket_option.html) |
| 12view_cart | 페이지뷰 – DOM 사용 가능 | Page Path 정규 표현식과 일치 | ^\/order\/basket.html |
3) 태그 입력
주요 전자상거래 이벤트인 view_item, add_to_cart, begin_checkout, purchase에 대한 태그 세팅입니다. 네이버 페이, 카카오 페이도 대응됩니다.
아래 이미지를 참고하여 태그 항목에 하나씩 추가해 주세요.
a. 맞춤 HTML 태그

태그명: view_item
스크립트:
<script>
dataLayer.push({
"event": "view_item",
"ecommerce": {
"currency": "KRW",
"User_ID": {{07User_ID}},
"items": {{01productInfo}}
}
});
</script>
트리거: 11view_detail
태그명: add_to_cart
스크립트:
<script>
dataLayer.push({
"event": "add_to_cart",
"ecommerce": {
"currency": "KRW",
"User_ID": {{07User_ID}},
"items": {{01productInfo}}
}
});
</script>
트리거: 03actionCart
태그명: begin_checkout
스크립트:
<script>
dataLayer.push({
"event": "begin_checkout",
"ecommerce": {
"User_ID": {{07User_ID}},
"currency": "KRW",
"items": {{02orderInfo}}
}
});
</script>
트리거: 04orderform
태그명: purchase
스크립트:
<script>
dataLayer.push({
"event": "purchase",
"ecommerce": {
"transaction_id": {{03orderId}},
"User_ID": {{07User_ID}},
"currency": "KRW",
"shipping": {{05shipFee}},
"value": {{04orderPrice}},
"items": {{06orderProductInfo}}
}
});
</script>
트리거: 05orderresult
태그명: npay_view_item
스크립트:
<script>
var dataLayer = dataLayer || [];
var actEcommerce = {{actionEcommerce}};
$(document).delegate( 'a[id^=NPAY_BUY_LINK_IDNC_ID_]', 'click', function(event){
var orderId = $(this).attr('id').replace(/^NPAY_BUY_LINK_IDNC_ID_(.*)/, '$1');
actEcommerce(event,'nPay_checkoutGA4', dataLayer, {'orderId':orderId});
});
$(document).delegate( '.__checkout_btn_buy', 'click', function(event){
var orderId = $('#checkoutContainer').data('id');
actEcommerce(event,'kakao_checkoutGA4', dataLayer, {'orderId':orderId});
});
</script>
트리거: 11 view_detail
태그명: npay_basket
스크립트:
<script>
var dataLayer = dataLayer || [];
var actEcommerce = {{actionEcommerce}};
var getPosition = {{getPositionFromBasket}};
// Naver Pay
$('a[id^=NPAY_BUY_LINK_IDNC_ID_]').bind('click', function(event){
var position = getPosition();
var orderId = $(this).attr('id').replace(/^NPAY_BUY_LINK_IDNC_ID_(.*)$/, '$1');
actEcommerce(event,'nPay_checkoutCartGA4', dataLayer, { 'position' : position, 'orderId': orderId} );
});
// kakao Pay
$('.__checkout_btn_buy').bind('click', function(event){
var position = getPosition();
var orderId = $('#checkoutContainer').data('id');
actEcommerce(event,'kakao_checkoutCartGA4', dataLayer, { 'position' : position, 'orderId': orderId} );
});
</script>
트리거: 12view_cart
b. GA4 이벤트 태그

✅ user_id는 필수 요소는 아니어서 설명에서 제외하였습니다. 또한 대부분 국내에서만 전자상거래가 이루어지기에, 통화 단위는 KRW로 고정하였습니다. 만약 해외에서도 전자상거래가 발생한다면 통화 단위의 변수 처리가 필요합니다.
| 태그 이름 | 이벤트 이름 | 매개변수 | 트리거 |
|---|---|---|---|
| view_item_event | view_item | items | {{01productInfo}} currency | KRW |
06view_item |
| add_to_cart_event | add_to_cart | items | {{01productInfo}} currency | KRW |
07add_to_cart |
| begin_checkout_event | begin_checkout | items | {{01productInfo}} currency | KRW |
08begin_checkout |
| purchase_event | purchase | currency | KRW items | {{06orderProductInfo}} shipping | {{05shipFee}} transaction_id | {{03orderId}} value | {{04orderPrice}} |
10dom+purchase |
| npay_purchase_event | purchase | currency | {{Ecommerce Currency}} items | {{Ecommerce Items}} shipping | {{Ecommerce Shipping}} transaction_id | {{Ecommerce Transaction ID}} value | {{Ecommerce Value}} payment_type | {{Ecommerce Payment Type}} |
13npay_purchase |
3. 유의할 점
1) GA4 전자상거래 지표는 인사이트 분석 용도로만 사용합니다.
GA4에서 추적되는 전자상거래 매출은 카페24 관리자나 PG사에서 확인 가능한 실제 매출과는 차이가 날 수 있습니다. (GA4 자체의 유실, 스크립트 오집계 등 다양한 원인으로 인해)
특히 네이버 페이나 카카오 페이와 같은 결제 매체는 사이트 외부에서 완료가 되기 때문에, 완료 전 이탈하는 경우에도 구매 이벤트로 추적되게 됩니다. (버튼 클릭 시 구매로 산정)
그렇기 때문에 GA4 전자상거래 데이터는 마케팅 효율 등 인사이트 분석 용도로 사용해야지, 실제 매출 관리 용도로 활용해서는 안됩니다.
다만, 실제 매출과 GA4 지표가 심하게 차이난다면 스크립트 등 기본 세팅에 문제가 있다고 볼 수 있습니다. 이 경우는 세팅 항목 점검이 필요합니다.
2) 사이트 구조, 업데이트 항목에 따라 실행 조건 등 세부 항목이 달라질 수 있습니다.
카페24의 경우 주문, 결제 등 주요 구조는 모든 자사몰이 동일한 구조이기에 큰 문제는 없으나, 사이트 구조나 향후 업데이트 여부에 따라 세부 항목이 달라질 수 있습니다.
마무리
이번 가이드에서는 카페24 자사몰의 전자상거래 데이터를 GA4와 연동하여 분석하는 방법을 알아보았습니다.
혹시 궁금한 점이나 추가적인 경험이 있다면 댓글 혹은 상담 문의를 통해 공유해 주세요!
알려주신대로 세팅을 하니까 각 이벤트가 중복으로 여러 번 집계 되는데 해결 방법이 있을까요?
ex) 장바구니 담기 1회만 해도 add_to_cart 이벤트 3회로 집계됨.
안녕하세요! 혹시 이벤트가 중복 집계될 때 장바구니만 문제가 있으신건가요, 아니면 다른 이벤트도 문제가 있으신가요? 장바구니의 경우는 각 사이트마다 트리거 세팅이 달라져야 합니다.