웹 클릭스트림 수집하기 3) 수집 로그 구체화
진짜 쓸모있는 로그 저장. JS 코드까지 적용하기
뭘 쌓을 것인가 정하기
로그 수집을 시작할 때는 구체적인 목적 없이 이벤트만 무작정 쌓다 보면, 나중에 데이터를 어떻게 활용해야 할지 막막해집니다.
이번 글에서는 분석할 주제를 명확히 정하고, 그에 따라 꼭 필요한 클릭스트림 로그만 골라 수집하는 과정을 소개합니다.
🎯 이번 글 목표
- “블로그 유입 경로별 인기 게시글 분석” 주제 확정
- 수집할 로그 목록 정의
- 프론트엔드 코드로 로그 수집 구현
1. 확정된 주제 소개
블로그를 운영하다 보면, 어떤 외부 채널에서 어떤 게시글로 방문이 몰리는지 파악하는 것이 중요합니다. 이 글에서는 어떤 경로에서 어떤 게시글이 가장 많은 관심을 받았는지를 명확하게 파악하기 위한 로그를 정의하고자 합니다.
1.1 왜 이 분석이 필요한가?
로그를 아무 목적 없이 무작정 쌓아두다 보면 나중에 데이터를 꺼내 분석할 때 도대체 무엇을 물어봐야 할지 막막해집니다.
처음에는 페이지뷰나 클릭 수 같은 단순 지표만 확인해도 충분해 보이지만 어느 순간에는 이 트래픽이 어디서 왔을지, 왜 어떤 게시글에만 반응이 몰리는 지 근본적인 질문이 떠오르죠.
이 글에서는 바로 이 지점을 짚어, 분석목표를 블로그 유입 경로별 인기 게시글 분석이라는 명목으로 필요한 로그만 골라 수집하는 과정을 보여드리고자 합니다. 그래서 수집된 로그를 이후 활용까지 구체적으로 보여줄 수 있는 확장 가능한 프로젝트가 될 수 있을 거에요!
핵심 주제: 블로그 유입 경로별 인기 게시글 분석
외부 채널(referrer)별로 어떤 게시글이 얼마나 많은 방문을 기록했는지 파악할 수 있는 전반적인 로그를 수집해봅시다.
2. 수집할 로그 목록
이벤트 | 내용 | 필드 |
---|---|---|
session_start |
사용자 세션 시작 시점 기록 | session_id, user_agent, referrer, timestamp |
referral_event |
외부 채널(referrer/UTM) 정보 수집 | session_id, referrer, utm_source, utm_medium, utm_campaign, timestamp |
page_view |
페이지 진입 기록 | page_url, referrer, session_id, timestamp |
time_on_page |
페이지 체류 시간 기록 | page_url, duration(ms), session_id, timestamp |
session_end |
사용자 세션 종료 시점 기록 | session_id, referrer, timestamp |
scroll_event |
스크롤 깊이(%) 기록 | page_url, scroll_percentage, session_id, timestamp |
click_event |
클릭 발생 위치 및 대상 기록 | element_id, page_url, x_coord, y_coord, session_id, timestamp |
navigation |
내부 링크(페이지 간 이동) 기록 | from_page, to_page, session_id, timestamp |
share_event |
공유 버튼 클릭 플랫폼 기록 | platform, page_url, session_id, timestamp |
comment_submit |
댓글 제출 이벤트 기록 | post_id, session_id, timestamp |
bounce_event |
비활성(바운스) 세션 기록 | page_url, session_id, timestamp |
참고:
referral_event
를 통해 첫 진입 시점의 UTM 파라미터나document.referrer
값을 별도로 수집하면
이후page_view
와 결합해 마케팅 캠페인의 효과를 더욱 정확히 분석할 수 있습니다.- 각 이벤트는 필요에 따라 추가 필드를 추가하거나 조정해 확장할 수 있습니다.
3. 프론트엔드 로그 수집 코드 구현
자, 이제 위와 같이 적용한 로그 목록에 맞게 프론트엔드에 삽입할 자바스크립트 코드를 다음과 같이 작성했습니다. (코드는 GPT🤖의 도움을 받아 작성하였습니다.)
3.1 sendLog
유틸리티 함수 작성
로그 전송 기본 구조 및 UTM/Referrer 병합 로직입니다. 가장 상단에는 ingest_url을 먼저 정의합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const LOG_ENDPOINT = "";
// ———— 유틸리티: 로그 전송 함수 ————
function sendLog(eventType, extra = {}) {
const utm = JSON.parse(sessionStorage.getItem('utm') || '{}');
const payload = {
event: eventType,
timestamp: new Date().toISOString(),
path: window.location.pathname,
referrer: document.referrer || null,
...utm,
...extra
};
// 언로드 시 세션 종료만 sendBeacon 사용
if (eventType === 'session_end' && navigator.sendBeacon) {
navigator.sendBeacon(LOG_ENDPOINT, JSON.stringify(payload));
} else {
fetch(LOG_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
keepalive: true
}).catch(err => console.error('Ingest error:', err));
}
}
3.2 외부 채널 & 세션 로그, 페이지 뷰
referral_event
(utm_source/utm_medium/utm_campaign, referrer)session_start
(session_id, user_agent, timestamp)page_view
(page_url, session_id, timestamp)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// ———— 1) UTM/Referrer 저장 & referral_event ————
window.addEventListener('load', () => {
// UTM 추출
const params = new URLSearchParams(location.search);
const utm = {
utm_source: params.get('utm_source'),
utm_medium: params.get('utm_medium'),
utm_campaign: params.get('utm_campaign')
};
if (utm.utm_source) {
sessionStorage.setItem('utm', JSON.stringify(utm));
sendLog('referral_event', {});
}
// 세션 시작 & 페이지뷰
sendLog('session_start', { user_agent: navigator.userAgent });
sendLog('page_view', {});
});
3.3 체류 시간 & 세션 종료
time_on_page
(duration, session_id, timestamp)session_end
(session_id, timestamp)
1
2
3
4
5
6
// ———— 2) 체류 시간 & 세션 종료 ————
const __startTime = Date.now();
window.addEventListener('beforeunload', () => {
sendLog('time_on_page', { duration: Date.now() - __startTime });
sendLog('session_end', {});
});
3.4 스크롤 깊이 로깅
scroll_event
(scroll_percentage, session_id, timestamp)
1
2
3
4
5
6
7
8
9
// ———— 3) 스크롤 깊이 로깅 ————
let __lastScroll = 0;
window.addEventListener('scroll', () => {
const now = Date.now();
if (now - __lastScroll < 1000) return;
__lastScroll = now;
const pct = Math.round((window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100);
sendLog('scroll_event', { scroll_percentage: pct });
});
3.5 클릭 이벤트 로깅
click_event
(element_id, x/y 좌표, session_id, timestamp)
1
2
3
4
5
6
7
8
9
10
11
12
// ———— 4) 클릭 이벤트 로깅 ————
document.addEventListener('click', e => {
const tgt = e.target.closest('[data-log-click]') || e.target;
// 👇 클릭 로그를 콘솔에도 출력
console.log('click_event:', { tag: tgt.tagName, id: tgt.id || null, x: e.clientX, y: e.clientY });
sendLog('click_event', {
tag: tgt.tagName,
id: tgt.id || null,
x: e.clientX,
y: e.clientY
});
});
3.6 내부 내비게이션 로깅
navigation
(from_page, to_page, session_id, timestamp)
1
2
3
4
5
6
7
8
9
// ———— 5) 내부 내비게이션 ————
document.querySelectorAll('a[href^="/"]').forEach(a => {
a.addEventListener('click', () => {
sendLog('navigation', {
from: window.location.pathname,
to: new URL(a.href).pathname
});
});
});
3.7 인터랙션 로그
share_event
(platform, session_id, timestamp)comment_submit
(post_id, session_id, timestamp)bounce_event
(session_id, timestamp)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// ———— 6) 기타 인터랙션 ————
// 공유 버튼
document.querySelectorAll('[data-log-share]').forEach(btn => {
btn.addEventListener('click', () => {
sendLog('share_event', { platform: btn.dataset.platform || null });
});
});
// 댓글 폼
const commentForm = document.getElementById('comment-form');
if (commentForm) {
commentForm.addEventListener('submit', () => {
sendLog('comment_submit', { post_id: commentForm.dataset.postId });
});
}
// 바운스 정의: 5초 내 상호작용 없으면
let __interacted = false;
['click','scroll','keydown'].forEach(evt =>
document.addEventListener(evt, () => { __interacted = true; })
);
setTimeout(() => {
if (!__interacted) sendLog('bounce_event', {});
}, 5000);
목표에 필요한 로그를 각각 수집하는 코드를 위와 같이 작성했습니다.
🚀 결론 및 다음 단계
이번 글에서는 “블로그 유입 경로별 인기 게시글 분석“을 위한 클릭스트림 로그를 정의했습니다.
- 코드 작성: UTM·referrer, 세션, 페이지뷰, 체류 시간, 스크롤, 클릭, 내비게이션, 공유·댓글·바운스까지 모든 핵심 이벤트를 수집하도록 Javascript 코드를 작성했습니다.
- 배포 완료: 수집 스크립트는
_includes/clickstream.html
파일을 모든 페이지에서 인클루드하여 적용하였으며, 로컬·프로덕션 환경 분기까지 처리되었습니다- 주요 변경사항은 커밋에서 확인할 수 있습니다.
다음 글에서는 이제 이렇게 정의한 로그를 바탕으로 AWS Lambda를 활용해 Kafka 프로듀싱, S3 배치 적재 과정을 중심으로 단계별 코드 예제와 Terraform 설정을 공유드리겠습니다.