홀리버스는 사용자의 기본 정보와 함께 이용정보를 통해 사용자 분석을 진행합니다. 따라서 이용정보를 클라이언트 로그를 통해 수집하도록 하였습니다.
1. 로깅 방식
로깅의 전과정은 다음과 같이 이뤄집니다.
- 특정 이벤트 시 로그 발생(특정 버튼 클릭, 스크롤)
- 필요한 데이터를 조합해 로그 형식으로 가공
- 로그를 IndexDB에 단건마다 적재
- 5초마다 IndexDB에 있는 로그들을 전송하고, 전송 완료시 IndexDB내의 로그 삭제. 만약 전송 실패시 로그를 삭제하지 않고 다음 주기때 재요청
위와 같은 과정을 거쳐 로그 수집 및 발송이 이루어지도록 설계한 과정을 설명하겠습니다.
2. 로깅 설계
먼저, 서버의 로깅에 대한 요구사항은 다음과 같았습니다.
- 로그의 형식에 맞게 보내줄것
- 네트워크 비용을 줄이기 위해 단건마다 보내는 것이 아닌 적재해두었다가 5초마다 전송할 것
request로 보내주어야하는 로그의 형식은 다음과 같습니다.

이 요구사항을 만족하기 위해 먼저 로깅의 과정을 크게 로그 수집, 로그 가공, 로그 적재, 로그 전송으로 나누어 설계를 시작하였습니다.
2.1 로그 수집, 가공
첫번째로 로그 수집을 생각하였을때, 특정 이벤트 로그 수집을 채택하였고 유의미한 사용자 행동 발생 시, onClick 핸들러 내부에서 로그 수집 로직이 실행되도록 설계했습니다.
다음으로 로그 가공의 경우 로그의 형식을 보았을때, 해당 이벤트에서 얻을 수 있는 정보와 그 외 공통 로그 정보로 나누어볼 수 있었습니다.
- 이벤트에서 얻는 상세 정보: event, event_name, event_properties
- 공통 로그 정보: event_id, timestamp
로그 정보(특히 공통 속성)를 말단 컴포넌트까지 전달하기 위해 Props를 사용할 경우, 중간 컴포넌트들이 불필요하게 로그 데이터를 전달해야 하는 Prop Drilling 문제가 발생합니다. 이는 코드의 가독성을 해치고 유지보수 비용을 증대시킵니다.
이를 방지하고자 전역적으로 로그의 정보를 관리할 것인지 먼저 고려하였습니다. 그러나 모든 로그 정보를 단일 전역 스토리지에서 관리할 경우, 특정 페이지나 섹션 단위의 독립적인 컨텍스트를 분리하여 관리하기가 복잡해지게 되었습니다.
이에 상위 컴포넌트에서 하위 컴포넌트로 데이터가 자연스럽게 상속되는 계층적 구조를 활용하고자 context를 사용하여 설계하였습니다. 이를 통해 페이지 레벨의 공통 정보(URL 등)는 상위 Provider에서 주입하고, 최하위 버튼 컴포넌트에서는 필요한 상세 이벤트 정보만 결합하여 로그를 완성할 수 있게 되었습니다.
2.2 로그 적재, 전송
로그를 적재하고 5초마다 전송후, 적재한 로그를 비울때 가장 중요한 것을 로그가 유실되지 않는 것입니다.
처음에는 구현이 간편한 LocalStorage를 고려했으나, 약 5MB 내외의 용량 제한이 있어 대량의 로그 적재에 부적절했습니다. 또한, 단순 텍스트 형태로 저장되어 보안상 취약하다는 단점이 있었습니다.
이에 IndexDB를 적용하였습니다. IndexDB는 다량의 데이터를 보관할 수 있고, 오프라인에서도 데이터가 유실되지 않으므로 사용하기 적합하다고 판단하였습니다. 이를 활용하여 5초마다 적재된 로그를 전송하도록 구현하였습니다.
