Voluum, Redtrack, ClickFlare에 A/B 테스트를 연동하는 방법
소개 디지털 마케팅에서 A/B 테스트는 캠페인을 성공시키는 데 필수입니다. LanderLab은 이제 같은 URL로 여러 변형을 번갈아 노출하는 서버 사이드 A/B 테스트를 설정할 수 있는 훌륭한 도구를 제공합니다.
소개
디지털 마케팅에서 A/B 테스트는 캠페인을 성공시키는 데 필수입니다. LanderLab은 이제 같은 URL로 여러 변형을 번갈아 노출하는 서버 사이드 A/B 테스트를 설정할 수 있는 훌륭한 도구를 제공합니다. 이번 업데이트로 사용자는 랜딩 페이지를 A/B 테스트하고, 선호하는 트래커에서 결과를 교차 확인할 수 있습니다.
LanderLab으로 A/B 테스트를 해야 하는 이유
대부분의 마케터는 ClickFlare, Voluum, Redtrack 같은 자체 트래킹 소프트웨어로 랜딩 페이지를 스플릿 테스트하며, 대부분의 경우 그것만으로 충분합니다. 하지만 트래커로는 스플릿 테스트를 할 수 없는 경우도 있습니다. 프리미엄 트래픽 소스는 리디렉션을 허용하지 않아, 최종 도착 URL이 랜딩 페이지 URL과 같아야 합니다.
이럴 때 LanderLab의 서버 사이드 A/B 테스트가 유용합니다. 흐름에서 리디렉션을 하나 없앨 수 있기 때문입니다. LanderLab에서 A/B 테스트가 어떻게 작동하는지 자세히 알고 싶다면 이 글을 확인해 보세요. 무엇보다 좋은 점은 LanderLab을 대부분의 최신 트래커와 연동할 수 있다는 것입니다.

개념은 간단합니다. LanderLab의 A/B 테스트를 모든 트래커가 제공하는 다이렉트 트래킹 방식과 결합하는 것입니다. 기본기를 다루기 위해, 이 글에서는 ClickFlare, Voluum, Redtrack으로 이 구성을 구현하는 방법을 안내합니다.
ClickFlare로 A/B 테스트 설정하기
ClickFlare의 다이렉트 트래킹 방식은 이 구성에 완벽한 조합이 되어 줍니다. 두 플랫폼은 같은 회사가 만들고 개발했으니, 둘을 함께 써서 더 긴밀하게 연동하는 것이 당연히 합리적입니다. ClickFlare가 제공하는 다이렉트 트래킹에 대해 더 알고 싶다면 이 글을 읽어 보세요.
이 구성을 구현할 때 따라야 할 기본 단계는 2가지이며, 같은 단계를 Voluum과 Redtrack에도 적용할 수 있습니다. 첫 번째 단계는 ClickFlare에서 variantID를 리포트하기 위한 새 트래킹 파라미터를 정의하는 방법입니다.

트래커에서 variantID를 추적하려면 다이렉트 트래킹 스크립트를 일부 수정해야 합니다. 아래 스크립트에는 교체해야 하는 변수가 2개 들어 있습니다. “REPLACE_ME_CAMPAIGN_ID”와 “REPLACE_ME_CUSTOM_DOMAIN”입니다. 준비가 끝나면 전체 코드를 LanderLab의 Settings > Custom Code > Header Section에 넣어야 합니다.
<script> ! function() {
"use strict";
var t = "lp_ref",
n = "cpid",
e = "lpurl",
r = "REPLACE_ME_CUSTOM_DOMAIN",
c = "(?<domain>http(?:s?)://[^/]*)".concat("/cf/click"),
a = "(?:(?:/(?<cta>[1-9][0-9]*)/?)|(?:/))?",
i = "^".concat(c).concat(a).concat("(?:$|(\\?.*))"),
o = 'javascript:window.clickflare.l="(?<original_link>'.concat(c).concat(a, '("|(\\?[^"]*"))).*'),
s = function() {
return new RegExp(i, "")
},
u = function() {
return new RegExp(o, "")
};
function l(t) {
var n = function(t) {
return t.replace(s(), (function(t) {
for (var n = [], e = 1; e < arguments.length; e++) n[e - 1] = arguments[e];
var c = n[n.length - 1].domain;
return t.replace(c, r)
}))
}(t);
return 'javascript:window.clickflare.l="'.concat(n, '"; void 0;')
}
function f(t, n) {
if (n && t && n.apply(document, [t]), /loaded|interactive|complete/.test(document.readyState))
for (var e = 0, r = document.links.length; e < r; e++)
if (s().test(document.links[e].href)) {
var c = document.links[e];
window.clickflare.links_replaced.has(c) || (c.href = l(c.href), window.clickflare.links_replaced.add(c))
}
}! function(c, a) {
var i = document.onreadystatechange;
window.clickflare || (window.clickflare = {
listeners: {},
customParams: {},
links_replaced: new Set,
addEventListener: function(t, n) {
var e = this.listeners[t] || [];
e.includes(n) || e.push(n), this.listeners[t] = e
},
dispatchEvent: function(t, n) {
n && (this.customParams[t] = n), (this.listeners[t] || []).forEach((function(t) {
return t(n)
}))
},
push: function(t, n) {
n && (this.customParams[t] = n), (this.listeners[t] || []).forEach((function(t) {
return t(n)
}))
}
}, document.onreadystatechange = function(t) {
return f(t, i)
}, f(null, i), setTimeout((function() {
! function(c, a) {
var i, o = function(c, a) {
var i = new URL("".concat(r).concat(c)),
o = "{",
s = o + o;
a.startsWith(s) || i.searchParams.set(n, a);
return i.searchParams.append(t, document.referrer), i.searchParams.append(e, location.href), i.searchParams.append("variantId", window.LL_VARIANT_ID), i.searchParams.append("lpt", document.title), i.searchParams.append("t", (new Date).getTime().toString()), i.toString()
}(c, a),
s = document.createElement("script"),
l = document.scripts[0];
s.async = 1, s.src = o, s.onerror = function() {
! function() {
for (var t = function(t, n) {
var e = document.links[t];
u().test(e.href) && setTimeout((function() {
e && e.setAttribute("href", function(t) {
var n = t.match(u());
if (n) {
var e = (n.groups || {}).original_link;
return e ? e.slice(0, -1) : t
}
return t
}(e.href))
}))
}, n = 0, e = document.links.length; n < e; n++) t(n)
}()
}, null === (i = l.parentNode) || void 0 === i || i.insertBefore(s, l)
}(c, a)
})))
}("".concat("/cf/tags", "/").concat(new URL(window.location.href).searchParams.get("cftmid") || "{{__CONTAINER_ID__}}"), new URL(window.location.href).searchParams.get(n) || "REPLACE_ME_CAMPAIGN_ID")
}(); </script>Voluum으로 A/B 테스트 설정하기
Voluum은 제휴 마케팅에서 가장 오래된 트래커 중 하나로, 지금도 시장에서 큰 비중을 차지하고 있습니다. 이 트래커에 variantID를 리포트할 때도 앞에서와 같은 방식으로 다이렉트 트래킹을 활용합니다.

VariantID는 트래픽 소스 템플릿의 맨 아래에 추가하지만, 어떤 순서로 넣어도 크게 상관없습니다. 용어를 통일하기 위해 교체해야 하는 변수 2개도 같습니다. “REPLACE_ME_CAMPAIGN_ID”와 “REPLACE_ME_CUSTOM_DOMAIN”입니다. 다음 코드를 복사해 LanderLab의 커스텀 코드 영역에 붙여 넣으면 설정이 완료됩니다.
<<b>style</b>>.dtpcnt{opacity: 0;}</<b>style</b>>
<<b>script</b>>
(function(d, c, k, l, r, t, g, u, A, e, m, v, B, a, n, p, h, q, w, D, x) {
function y() { for (var f = c.querySelectorAll(".dtpcnt"), b = 0, a = f.length; b < a; b++) f[b][u] = f[b][u].replace(/(^|\s+)dtpcnt($|\s+)/g, "") }
function C(a, b, d, e) { var f = new Date;
f.setTime(f.getTime() + (e || 864E5));
c.cookie = a + "=" + b + "; " + d + "samesite=Strict; expires=" + f.toGMTString() + "; path=/";
k.setItem(a, b);
k.setItem(a + "-expires", f.getTime()) }
function z(a) {
var b = c.cookie.match(new RegExp("(^| )" + a + "=([^;]+)"));
return b ? b.pop() : k.getItem(a + "-expires") && +k.getItem(a +
"-expires") > (new Date).getTime() ? k.getItem(a) : null
}
x = "https:" === d.location.protocol ? "secure; " : "";
d[e] || (d[e] = function() {
(d[e].q = d[e].q || []).push(arguments) }, p = c[r], c[r] = function() { p && p.apply(this, arguments); if (d[e] && !d[e].hasOwnProperty("params") && /loaded|interactive|complete/.test(c.readyState))
for (; a = c[t][m++];) /\/?click\/?($|(\/[0-9]+)?$)/.test(a.pathname) && (a[g] = "javascrip" + d.postMessage.toString().slice(4, 5) + ":" + e + '.l="' + a[g] + '",void 0') }, setTimeout(function() {
(q = /[?&]cpid(=([^&#]*)|&|#|$)/.exec(d.location.href)) &&
q[2] && (h = q[2], w = z("vl-" + h));
var f = z("vl-cep"),
b = location[g];
if ("savedCep" === B && f && (!h || "undefined" === typeof h) && 0 > b.indexOf("cep=")) { var e = -1 < b.indexOf("?") ? "&" : "?";
b += e + f } a = c.createElement("script");
n = c.scripts[0];
a.defer = 1;
a.src = v + (-1 === v.indexOf("?") ? "?" : "&") + "lpref=" + l(c.referrer) + "&lpurl=" + l(b) + "&lpt=" + l(c.title) + "&vtm=" + (new Date).getTime() + (w ? "&uw=no" : "") + "&variantId=" + window.LL_VARIANT_ID ;
a[A] = function() { for (m = 0; a = c[t][m++];) /dtpCallback\.l/.test(a[g]) && (a[g] = decodeURIComponent(a[g]).match(/dtpCallback\.l="([^"]+)/)[1]);
y() };
n.parentNode.insertBefore(a, n);
h && C("vl-" + h, "1", x)
}, 0), setTimeout(y, 7E3))
})(window, document, localStorage, encodeURIComponent, "onreadystatechange", "links", "href", "className", "onerror", "dtpCallback", 0, "REPLACE_ME_CUSTOM_DOMAIN/d/REPLACE_ME_CAMPAIGN_ID.js", "savedCep");
</<b>script</b>>
<<b>noscript</b>>
<<b>link</b> href="REPLACE_ME_CUSTOM_DOMAIN/d/REPLACE_ME_CAMPAIGN_ID.js?noscript=true&lpurl=" rel="stylesheet" /></<b>noscript</b>>Redtrack으로 A/B 테스트 설정하기
마지막으로 같은 구성을 Redtrack에도 구현해 보겠습니다. 이 트래커는 시장에서 상당한 비중을 차지하며, ClickFlare나 Voluum과 같은 기능을 제공합니다.
다만 나머지 두 트래커와 달리, 일부 토큰은 시스템 토큰 전용이기 때문에 variantID를 특정 토큰에 할당해야 합니다. 이 예시에서는 variantID를 sub18에 할당했고, 다이렉트 트래킹 스크립트에도 똑같이 반영됩니다.

“REPLACE_ME_CAMPAIGN_ID”와 “REPLACE_ME_CUSTOM_DOMAIN”을 교체하면 코드를 LanderLab에서 바로 사용할 수 있습니다.
<script>
function getCookie(name) {
var value = "; " + document.cookie;
var parts = value.split("; " + name + "=");
if (parts.length === 2) {
return parts.pop().split(";").shift();
}
}
var trackerURL = "REPLACE_ME_CUSTOM_DOMAIN";
var campaignID = "REPLACE_ME_CAMPAIGN_ID";
var cachebuster = Math.round(new Date().getTime() / 1000);
var rtkClickID;
var rtkfbp = getCookie('_fbp') || '';
var rtkfbc = getCookie('_fbc') || '';
var LL_VARIANT_ID = window.LL_VARIANT_ID;
var locSearch = window.location.search;
var urlParams = new URLSearchParams(locSearch);
var pixelParams = "&" + locSearch.substr(1) + "&sub18=" + LL_VARIANT_ID + "&sub19=" + rtkfbp + "&sub20=" + rtkfbc
if (campaignID == "") {
campaignID = urlParams.get('rtkcmpid')
}
var initialSrc = "https://"+ trackerURL + "/" + campaignID + "?format=json";
function stripTrailingSlash(str) {
return str.replace(/\/$/, "");
}
var rawData;
function fixHrefWithClick(
_rawData,
_cachebuster,
_rtkClickID
) {
document.querySelectorAll('a').forEach(function(el) {
if (el.href.indexOf(trackerURL + "/click") > -1) {
if (el.href.indexOf('?') > -1) {
el.href = stripTrailingSlash(el.href) + "&clickid=" + (_rtkClickID || _rawData.clickid) + "&rtkck=" + _cachebuster
} else {
el.href = stripTrailingSlash(el.href) + "?clickid=" + (_rtkClickID || _rawData.clickid) + "&rtkck=" + _cachebuster
}
}
if (el.href.indexOf("clickid={clickid}") > -1) {
el.href = el.href.replace(/{clickid}/, _rawData.clickid) + "&rtkck=" + _cachebuster;
}
});
}
setTimeout(function() {
if (!urlParams.get('rtkcid')) {
xhr = new XMLHttpRequest;
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
rawData = JSON.parse(xhr.responseText);
rtkClickID = rawData.clickid
setCookie();
// fixHrefWithClick(rawData, cachebuster)
document.querySelectorAll('a').forEach(function(el) {
if (el.href.indexOf(trackerURL + "/click") > -1) {
if (el.href.indexOf('?') > -1) {
el.href = stripTrailingSlash(el.href) + "&clickid=" + rawData.clickid + "&rtkck=" + cachebuster
} else {
el.href = stripTrailingSlash(el.href) + "?clickid=" + rawData.clickid + "&rtkck=" + cachebuster
}
}
if (el.href.indexOf("clickid={clickid}") > -1) {
el.href = el.href.replace(/{clickid}/, rawData.clickid) + "&rtkck=" + cachebuster;
}
});
xhrr = new XMLHttpRequest;
xhrr.open("GET", "https://"+ trackerURL +"/view?clickid=" + rawData.clickid)
xhrr.send();
}
}
xhr.open("GET", initialSrc + pixelParams)
xhr.send();
} else {
rtkClickID = urlParams.get('rtkcid')
setCookie();
xhrTrack = new XMLHttpRequest;
xhrTrack.open("GET", "https://"+trackerURL+"/view?clickid=" + rtkClickID)
xhrTrack.send();
// fixHrefWithClick(rawData, cachebuster, rtkClickID)
document.querySelectorAll('a').forEach(function(el) {
if (el.href.indexOf(trackerURL+ "/click") > -1) {
if (el.href.indexOf('?') > -1) {
el.href = stripTrailingSlash(el.href) + "&clickid=" + rtkClickID + "&rtkck=" + cachebuster
} else {
el.href = stripTrailingSlash(el.href) + "?clickid=" + rtkClickID + "&rtkck=" + cachebuster
}
}
if (el.href.indexOf("clickid={clickid}") > -1) {
el.href = el.href.replace(/{clickid}/, rawData.clickid) + "&rtkck=" + cachebuster;
}
});
}
}, 5e1)
function setCookie() {
var cookieName = "rtkclickid-store",
cookieValue = rtkClickID,
expirationTime = 86400 * 30 * 1000,
date = new Date(),
dateTimeNow = date.getTime();
date.setTime(dateTimeNow + expirationTime);
var date = date.toUTCString();
document.cookie = cookieName + "=" + cookieValue + "; expires=" + date + "; path=/;"
}
</script>결론
이제 데이터가 트래커에 리포트되므로 특정 변형이 이벤트를 얼마나 가져오는지 실제로 측정할 수 있습니다. 승리한 변형을 알게 되면 A/B 테스트를 종료하고 그 변형을 메인 변형으로 설정하면 됩니다. 차단당할 위험 없이 오늘부터 LanderLab으로 스플릿 테스트를 시작해 보세요.



