개인정보보호법에 어긋나지 않도록 UserId를 암호화 하여 수집해봅시다.
안녕하세요. 인트렌치 컨설팅 조유하 입니다.
구글 애널리틱스에서 User-id 보기를 이용하기 위해서는 로그인한 User의 Id를 수집해야 하는데요.
개인 정보 보호법*과 구글에서 제공하는 개인 식별 정보(PII) 전송 방지를 위한 권장사항*에 따르면 개인 식별이 가능한 정보 수집을 금지하고 있습니다.
그러나 이것을 수집할 수 있도록 하는 방법이 있는데 그것이 바로 암호화(가명 처리)입니다.
암호화는 최소 SHA256(복원이 불가능한 해시함수. 암호화 기법), 8자 이상의 솔트(암호 처리 기법)를 사용해야 하며 이러한 방법으로 User Id를 암호화하여 수집해야 합니다.
※ 개인정보에 해당하는 항목과 최소 암호화 조건에 대해서는 아래 참고 URL에서 확인 가능합니다.
l 개인 식별 정보(PII) 전송 방지를 위한 권장사항 * 참고 URL 클릭
l 구글에서 개인 식별 정보로 간주하는 정보 * 참고 URL 클릭
이것을 적용할 때 가장 좋은 방법은 개발팀의 지원을 받아 적용하는 것인데요,
따로 개발팀이 존재하지 않아 지원이 어려운 임대몰의 경우에는 조금 다른 방법으로 적용할 수 있습니다.
먼저 개발팀의 지원이 가능한 경우부터 보겠습니다.
임대몰의 경우 2번부터 보시면 됩니다.
1. 개발팀 지원 가능한 경우
HTML의 <head>에 삽입한 GTM 컨테이너 바로 위에 한 가지 구문([스크립트 1])만 추가하면 되는데요.
방법은 간단합니다.. 지원 요청을 하실 때
“구글 PII(개인 식별정보) 정책에 따라 구글 애널리틱스에 암호화된 userId를 수집해야 하는데 sha256으로 [스크립트 1]를 참고하여 작업 부탁드립니다.”라고 전달하시면 됩니다.
[스크립트 1] 예시
<script>
window.dataLayer = window.dataLayer || [];
if ( 로그인한 경우 ){
dataLayer.push({ 'userId': '암호화한 userId' });
} else if ( 로그아웃 한 경우 ){
dataLayer.push({ 'userId': undefined });
}
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM 컨테이너 ID);
</script>
</head>
-> GTM이 로드되기 전, 공통 dataLayer 영역에 userId가 존재해야만 페이지뷰가 동작할 때 암호화된 정보를 GA에 전송할 수 있습니다. 스크립트의 위치는 </head> 위입니다.
-> 이미 GTM 컨테이너가 삽입되어 있다면 바로 위에 if, elseif 구문만 추가해 주시면 됩니다.
이렇게 스크립트를 삽입하면 로그인을 했을 때 [이미지 1]과 같은 형태로 dataLayer에 user id가 담기게 됩니다.
[이미지 1] 로그인 후의 dataLayer
->크롬 개발자 도구( F12 ) console 에서 dataLayer를 입력하면 확인이 가능합니다.
User Id가 해당 이미지처럼 적재되어 있지 않다면 시점 조정을 요청하셔야 합니다.
여기서 페이지뷰가 동작하는 시점은 event : ”gtm.js”입니다.
[이미지 1]같이 확인되었다면 GTM에서 GA로 전송하는 작업을 할 차례입니다.
먼저 GTM에서 DataLayer 변수를 만들어 줍니다.
(저는 변수 이름을 {{getDataLayerUserId}}로 하였습니다. (변수 이름은 자유롭게 입력하시면 됩니다.)
[이미지 2]
-> [스크립트 1]에서 push한 userId의 값을 가져오는 변수입니다.
1번으로 진행하였다면 2번은 건너뛰세요.
2. 임대몰(ex. 카페24, 고도몰, 메이크샵)의 경우
고도몰, 메이크샵, 카페24를 통해 테스트해 본 결과 세 가지의 임대몰 모두 유사한 방법으로 수집이 가능합니다.
카페24를 예시로 임대몰에서 userId를 암호화해 보겠습니다.
1) 먼저 관리자 계정으로 로그인합니다.
2) 내부 디자인 스크립트 수정으로 접근합니다.
[이미지 3] 디자인 수정 접근
3) 메인 레이아웃과 공통 레이아웃의 <body> 바로 아래에 로그인 user Id에 해당하는 치환 코드(가상 태그)를 삽입한다. (pc/mobile 모두 적용합니다.) [이미지 4] 참고
* 각 임대몰 별로 특정 값을 가지고 있는 치환코드(가상태그)가 있습니다.
* (카페24 : 모듈, 고도몰 : 치환코드, 메이크샵 : 가상태그)
* 카페24는 module에 맞춰 치환코드를 사용할 수 있습니다.
카페24 모듈 참고 URL ( http://sdsupport.cafe24.com/product/list.html?cate_no=61 )
[스크립트 2]
<div module="Layout_stateLogon" class="userId" style="display:none;">{$id}</div>
-> 카페 24에서 로그인 한 User Id를 가지는 모듈은 module="Layout_stateLogon", 키 값은 {id} 입니다.
-> 카페24가 아닌 다른 임대몰은 module="Layout_stateLogon"는 지우고 {id}를 해당 임대몰에서 제공하는 치환코드(가상태그)로 수정하시면 됩니다.
[이미지 4]
4) 삽입한 [스크립트 2]의 요소를 추적하고 암호화하는 변수를 만듭니다. GTM 변수 유형은 Custom Javascript 입니다.
[이미지 5]
[스크립트 3]
function(){
var userId= jQuery('.userId ').text().trim();
if ( userId == '' ){
userId = SHOP.getAdminID(); //해당 라인은 카페24에만 존재하는 값입니다. 다른 임대몰은 삭제해 주세요.
}
if ( userId != '' ){
return SHA256(userId);
} else {
return undefined;
}
function SHA256(s){
var chrsz = 8;
var hexcase = 0;
function safe_add (x, y) {
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xFFFF);
}
function S (X, n) { return ( X >>> n ) | (X << (32 - n)); }
function R (X, n) { return ( X >>> n ); }
function Ch(x, y, z) { return ((x & y) ^ ((~x) & z)); }
function Maj(x, y, z) { return ((x & y) ^ (x & z) ^ (y & z)); }
function Sigma0256(x) { return (S(x, 2) ^ S(x, 13) ^ S(x, 22)); }
function Sigma1256(x) { return (S(x, 6) ^ S(x, 11) ^ S(x, 25)); }
function Gamma0256(x) { return (S(x, 7) ^ S(x, 18) ^ R(x, 3)); }
function Gamma1256(x) { return (S(x, 17) ^ S(x, 19) ^ R(x, 10)); }
function core_sha256 (m, l) {
var K = new Array(0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1,
0x923F82A4, 0xAB1C5ED5, 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3,
0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, 0xE49B69C1, 0xEFBE4786,
0xFC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,
0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147,
0x6CA6351, 0x14292967, 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13,
0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, 0xA2BFE8A1, 0xA81A664B,
0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,
0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A,
0x5B9CCA4F, 0x682E6FF3, 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208,
0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2);
var HASH = new Array(0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F,
0x9B05688C, 0x1F83D9AB, 0x5BE0CD19);
var W = new Array(64);
var a, b, c, d, e, f, g, h, i, j;
var T1, T2;
m[l >> 5] |= 0x80 << (24 - l % 32);
m[((l + 64 >> 9) << 4) + 15] = l;
for ( var i = 0; i<m.length; i+=16 ) {
a = HASH[0];
b = HASH[1];
c = HASH[2];
d = HASH[3];
e = HASH[4];
f = HASH[5];
g = HASH[6];
h = HASH[7];
for ( var j = 0; j<64; j++) {
if (j < 16) W[j] = m[j + i];
else W[j] = safe_add(safe_add(safe_add(Gamma1256(W[j - 2]), W[j - 7]), Gamma0256(W[j - 15])), W[j - 16]);
T1 = safe_add(safe_add(safe_add(safe_add(h, Sigma1256(e)), Ch(e, f, g)), K[j]), W[j]);
T2 = safe_add(Sigma0256(a), Maj(a, b, c));
h = g;
g = f;
f = e;
e = safe_add(d, T1);
d = c;
c = b;
b = a;
a = safe_add(T1, T2);
}
HASH[0] = safe_add(a, HASH[0]);
HASH[1] = safe_add(b, HASH[1]);
HASH[2] = safe_add(c, HASH[2]);
HASH[3] = safe_add(d, HASH[3]);
HASH[4] = safe_add(e, HASH[4]);
HASH[5] = safe_add(f, HASH[5]);
HASH[6] = safe_add(g, HASH[6]);
HASH[7] = safe_add(h, HASH[7]);
}
return HASH;
}
function str2binb (str) {
var bin = Array();
var mask = (1 << chrsz) - 1;
for(var i = 0; i < str.length * chrsz; i += chrsz) {
bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (24 - i%32);
}
return bin;
}
function Utf8Encode(string) {
string = string.replace(/\r\n/g,"\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
}
else if((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
} else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
}
function binb2hex (binarray) {
var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
var str = "";
for(var i = 0; i < binarray.length * 4; i++) {
str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) +
hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF);
}
return str;
}
s = Utf8Encode(s);
return binb2hex(core_sha256(str2binb(s), s.length * chrsz));
}
}
-> 해당 스크립트를 복사, 붙여 넣기 해서 변수를 만듭니다.
** 이후의 단계는 1,2 모두 동일합니다. **
다음은 GA 설정 변수를 만들고 필드 설정 영역의 Field Name에는 userId, Value에는 [이미지 2 or 5]에서 생성한 변수를 추가합니다.
[이미지 6]
->Tracking Id는 구글 애널리틱스의 속성 ID를 입력합니다. (ex, UA-000000000-1)
이제 페이지뷰 태그를 만들어서 [이미지 6]에서 생성한 변수를 추적 세팅합니다.
[이미지 7]
여기까지 하셨으면 세팅은 끝났습니다.
그럼 의도한 대로 정보를 전송하는지 GTM 컨테이너가 삽입되어있는 사이트에서 확인해 보겠습니다.
GTM 미리보기를 실행하고 사이트에 접근합니다.
[이미지 8]
->Container Loaded시점(페이지뷰= event->gtm.js)에 [이미지 7]에서 생성한 태그가 동작합니다.
크롬 확장 프로그램인 WASP를 사용하여 데이터가 전송되는지 확인해 보겠습니다.
(WASP의 이용이 어려운 경우는 다른 확장 프로그램 GA Debug를 이용하여 확인할 수 있습니다.)
WASP : https://chrome.google.com/webstore/detail/niaoghengfohplclhbjnjheodgkejpih
GA Debug : https://chrome.google.com/webstore/detail/google-analytics-debugger/jnkmfdileelhofjcijamephohjechhna
[이미지 9]
SHA256으로 암호화한 경우는 이러한 형태로 GA에 전송합니다.
-> SHA256으로 암호화한 경우는 [이미지 10]과 같은 형태로 GA에 전송합니다.
[이미지 10]
암호화에 사용된 SHA256 함수 스크립트
[스크립트 4] Javascript SHA-256
function SHA256(s){
var chrsz = 8;
var hexcase = 0;
function safe_add (x, y) {
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xFFFF);
}
function S (X, n) { return ( X >>> n ) | (X << (32 - n)); }
function R (X, n) { return ( X >>> n ); }
function Ch(x, y, z) { return ((x & y) ^ ((~x) & z)); }
function Maj(x, y, z) { return ((x & y) ^ (x & z) ^ (y & z)); }
function Sigma0256(x) { return (S(x, 2) ^ S(x, 13) ^ S(x, 22)); }
function Sigma1256(x) { return (S(x, 6) ^ S(x, 11) ^ S(x, 25)); }
function Gamma0256(x) { return (S(x, 7) ^ S(x, 18) ^ R(x, 3)); }
function Gamma1256(x) { return (S(x, 17) ^ S(x, 19) ^ R(x, 10)); }
function core_sha256 (m, l) {
var K = new Array(0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1,
0x923F82A4, 0xAB1C5ED5, 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3,
0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, 0xE49B69C1, 0xEFBE4786,
0xFC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,
0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147,
0x6CA6351, 0x14292967, 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13,
0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, 0xA2BFE8A1, 0xA81A664B,
0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,
0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A,
0x5B9CCA4F, 0x682E6FF3, 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208,
0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2);
var HASH = new Array(0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F,
0x9B05688C, 0x1F83D9AB, 0x5BE0CD19);
var W = new Array(64);
var a, b, c, d, e, f, g, h, i, j;
var T1, T2;
m[l >> 5] |= 0x80 << (24 - l % 32);
m[((l + 64 >> 9) << 4) + 15] = l;
for ( var i = 0; i<m.length; i+=16 ) {
a = HASH[0];
b = HASH[1];
c = HASH[2];
d = HASH[3];
e = HASH[4];
f = HASH[5];
g = HASH[6];
h = HASH[7];
for ( var j = 0; j<64; j++) {
if (j < 16) W[j] = m[j + i];
else W[j] = safe_add(safe_add(safe_add(Gamma1256(W[j - 2]), W[j - 7]), Gamma0256(W[j - 15])), W[j - 16]);
T1 = safe_add(safe_add(safe_add(safe_add(h, Sigma1256(e)), Ch(e, f, g)), K[j]), W[j]);
T2 = safe_add(Sigma0256(a), Maj(a, b, c));
h = g;
g = f;
f = e;
e = safe_add(d, T1);
d = c;
c = b;
b = a;
a = safe_add(T1, T2);
}
HASH[0] = safe_add(a, HASH[0]);
HASH[1] = safe_add(b, HASH[1]);
HASH[2] = safe_add(c, HASH[2]);
HASH[3] = safe_add(d, HASH[3]);
HASH[4] = safe_add(e, HASH[4]);
HASH[5] = safe_add(f, HASH[5]);
HASH[6] = safe_add(g, HASH[6]);
HASH[7] = safe_add(h, HASH[7]);
}
return HASH;
}
function str2binb (str) {
var bin = Array();
var mask = (1 << chrsz) - 1;
for(var i = 0; i < str.length * chrsz; i += chrsz) {
bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (24 - i%32);
}
return bin;
}
function Utf8Encode(string) {
string = string.replace(/\r\n/g,"\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
}
else if((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
} else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
}
function binb2hex (binarray) {
var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
var str = "";
for(var i = 0; i < binarray.length * 4; i++) {
str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) +
hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF);
}
return str;
}
s = Utf8Encode(s);
return binb2hex(core_sha256(str2binb(s), s.length * chrsz));
}
-> 공개된 해시함수. 출처 URL( http://www.webtoolkit.info/javascript_sha256.html#.Xv2p7yj7QuX )
이렇게 하면 구글 애널리틱스 user-Id 보기에 데이터를 쌓기 위한 모든 작업이 끝났습니다.
이제 GA 실시간 리포트에서 확인을 해 보겠습니다.
[이미지 11]
-> user-Id 보기가 없다면 생성해야 합니다.
참고 URL( https://support.google.com/analytics/answer/3123666?hl=ko&ref_topic=3123660 )
이상. User Id 암호화부터 적용까지 알아보았습니다.
※ GTM에서 작업하신 내역은 꼭 제출(submit) 해야 합니다!
인트렌치 컨설팅이 어떤 회사인지 궁금하신가요?
그로스 해킹을 통해 서비스 전환율을 개선하세요.