사용자 행동을 추적해보자
이전 글에서 서버 사이드 로그만으로는 Next.js 애플리케이션에서 사용자의 실제 행동을 추적할 수 없다는 것을 깨달았다. 이번 글에서는 클라이언트 사이드 로깅 시스템을 직접 구현한 과정을 정리한다.
클라이언트에서 수집하고자 하는 데이터를 명확히 정의했다.
각 수집 항목별로 구현 방법을 정리했다.
logger 클래스를 활용trace_id를 클라이언트에서 부여utm 파라미터 파싱document.referrer로 이전 페이지 확인 가능
Referrer-Policy 헤더에 따라 제한될 수 있음layout 컴포넌트에 Analytics 컴포넌트를 삽입하여 사용자의 Route 변화를 추적한다. 모든 페이지는 layout 컴포넌트의 children으로 렌더링되기 때문에 전역적으로 추적이 가능하다.
trace_id를 어떻게 관리할지 고민이 필요했다.
선택지:
결정: 세션 스토리지 사용
세션 스토리지를 선택한 이유:
document.visibilityState API를 활용해 탭이 비활성화되면 측정을 중단하도록 구현할 수 있다. 하지만 현재 구현에서는 유저 이벤트 발생 시에만 로그를 수집하기 때문에, 비활성 상태 체크를 추가로 적용하지 않았다.
최종 세션 시간을 전송하기 위해 beforeunload 이벤트에서 navigator.sendBeacon()을 사용한다. 일반 fetch는 페이지 종료 시 취소될 수 있지만, sendBeacon은 안정적으로 전송된다.

기존 unified-logger에서 클라이언트 사이드 로깅을 위해 user-analytics를 분리했다.
unified-logger (서버 사이드)
└── 기존 로깅 로직
user-analytics (클라이언트 사이드)
├── 배치 전송 로직
├── 세션 ID 관리
├── beforeunload 처리
└── 이벤트 큐 관리
서버 로그용 /api/logs와 별도로 클라이언트 분석용 /api/analytics 엔드포인트를 만들었다.
sessionStorage로 유저의 세션 ID 관리beforeunload에서 sendBeacon 사용으로 안정적 전송수집된 데이터를 시각화하기 위해 Grafana 대시보드를 구성했다. JSON 파일로 대시보드 설정을 관리하면 버전 관리도 가능하다.
대시보드 파일은 Claude Code를 통해 생성했다.
최종적으로 정리한 로그 시스템의 역할 분담:
| 구분 | 서버 로그 (Nginx, Middleware) | 클라이언트 로그 (Analytics) |
|---|---|---|
| 목적 | 인프라 모니터링 | 사용자 행동 분석 |
| 수집 내용 | HTTP 상태 코드, 응답 시간, 에러 | 페이지뷰, 세션 시간, 커스텀 이벤트 |
| 특징 | 모든 요청 기록 (봇 포함 가능) | 실제 사용자 행동만 기록 |
SPA 환경에서의 로깅은 다르다: 전통적인 서버 로그 방식으로는 SPA의 클라이언트 라우팅을 추적할 수 없다.
목적에 맞는 계층에서 데이터를 수집하자: 서버 모니터링은 서버에서, 사용자 분석은 클라이언트에서.
프라이버시를 고려한 설계: 쿠키 동의 없이도 기본적인 분석이 가능하도록 세션 스토리지를 활용했다.
배치 처리의 중요성: 이벤트마다 네트워크 요청을 보내면 성능에 악영향을 준다. 배치 처리로 효율성을 높일 수 있다.