Part 0. 로그 전체 파이프라인 구축기
저희 서비스에서는 사용자의 행동을 수집하고, 이를 기반으로 고객의 특성을 정의하고 분류하고 추천하는 서비스를 제공하고자 했습니다.
사용자는 Holliverse 사이트에서 요금제를 비교하고, 변경하고, 위약금 페이지를 방문하는 등 다양한 행동을 합니다. 저희는 이러한 행동 로그를 수집하여 단순 통계로 끝내는 것이 아니라, 고객의 특성 정의, 캐릭터 분류, 추천 로직 개선까지 연결하고자 했습니다.
또한 서비스에는 최근 본 상품 조회 기능도 있었기 때문에, 이 로그를 어디에 어떻게 적재할지, 어떤 형태로 보관할지에 대한 고민도 많았습니다. 나아가, 이 로그들이 결국 캐릭터와 세그먼트 판정에도 영향을 주기 때문에, 단순 저장이 아니라 **어떻게 수집하고, 추출하고, 변환할 것인지(ETL)** 에 대한 전체 파이프라인 설계가 필요했습니다.
그러다 보니 자연스럽게 아래와 같은 질문이 생겼습니다.
- Where: 로그를 어디에 저장해야 할 것인가?
- How: 로그를 어떻게 수집할 것인가?
- 그리고 이 로그들을 어떻게 활용할 것인가?
이 문서에서는 이 질문에 대한 답을 하나씩 찾아간 과정을 정리합니다.
1. 어떤 로그를 어떻게 수집할 것인가
가장 먼저 고민한 것은 “무슨 로그를 수집할 것인가”, 그리고 “그 로그를 프론트에서 수집할 것인가, 백엔드에서 수집할 것인가” 였습니다.
저희는 서비스 로그를 크게 두 가지로 나누어 보았습니다.
- 클라이언트(Frontend) 로그
- 서버(Backend) 로그
처음에는 서버 로그만으로도 충분하지 않을까 고민했습니다. 하지만 저희 서비스의 핵심은 단순히 “무엇이 완료되었는가”만 아는 것이 아니라, 사용자가 어떤 화면에서 어떤 상품을 눌렀는지, 어떤 관심 흐름을 보였는지, 어디서 망설였는지까지 파악하는 것이었습니다.
따라서 프론트 로그와 서버 로그의 목적을 다음과 같이 구분하여 보았습니다.
1.1 프론트 로그와 백엔드 로그의 차이
| 구분 | 클라이언트(Frontend) 로그 | 서버(Backend) 로그 |
|---|---|---|
| 주요 목적 | 어떻게 행동했는가 (UI/UX 경험) | 무엇을 완료했는가 (최종 결과) |
| 수집 데이터 | 클릭, 스크롤, 체류 시간, 화면 진입, 버튼 노출 | API 호출, DB 트랜잭션, 결제 완료, 인증 |
| 장점 | 사용자의 미세한 인터랙션 파악 가능 | 데이터가 정확하고 위변조 위험이 적음 |
| 단점 | 네트워크 환경에 따라 누락 발생 가능 | 사용자가 버튼을 눌렀으나 실패한 경우 등 ‘과정’ 파악 어려움 |
이 비교를 통해 저희는 서버 로그만으로는 부족하다고 판단했습니다. 예를 들어 서버 로그만 보면 “변경 성공” 여부는 알 수 있지만, 사용자가 어떤 페이지에서 어떤 상품을 오래 보았는지, 비교를 했는지, 위약금 페이지를 확인했는지 같은 행동의 맥락을 파악하기 어렵기 때문입니다.
그래서 최종적으로는, 사용자 행동 분석과 추천·캐릭터 분류를 위한 핵심 로그는 프론트에서 수집하기로 결정했습니다.
프론트에서 로그 수집 방법은 다음을 참고해주세요. (클라이언트 로깅 설계 및 구현)
2. 로그 설계의 기본 원칙
로그를 수집하기로 결정한 뒤에는, 어떤 원칙으로 로그를 설계할 것인지 정리해야 했습니다. 무분별하게 이벤트를 찍기 시작하면 나중에 분석도 어렵고, 저장 비용만 증가하며, 무엇보다 추천에 활용하기 어려운 데이터가 됩니다.
저희가 로그 설계 시 세운 기본 원칙은 다음과 같습니다.
2.1 수집 목적을 먼저 정의하기
어떤 로그를 왜 수집하는지, 그리고 그 로그가 누구를 위해 필요한지를 먼저 정의해야 했습니다.
예를 들어,
- 결제 페이지 로그는 전환 퍼널 분석 목적
- 추천 클릭 로그는 추천 모델 성능 평가 목적
- 비교 버튼 클릭 로그는 고객의 탐색 성향 판단 목적
처럼 각 로그마다 수집 이유가 명확해야 했습니다.
즉, 로그는 “찍을 수 있으니까 찍는 것”이 아니라, 나중에 어떤 의사결정에 쓰일지를 먼저 정의한 뒤 설계해야 한다고 판단했습니다.
2.2 일관된 데이터 구조 유지하기
로그마다 필드명, 타입, 포맷이 제각각이면 분석과 ETL 과정에서 큰 문제가 생깁니다.
예를 들어 아래와 같은 불일치는 실제 개발에서 자주 발생할 수 있습니다.
user_idvsuidtimestampvscreated_atYYYY-MM-DDvstimestamp(ms)
이런 문제를 막기 위해 저희는 공통 필드 구조를 최대한 통일하기로 했습니다.
예를 들어 모든 로그에 공통적으로 아래 필드를 포함하도록 설계했습니다.
event_idtimestampeventevent_namemember_idevent_properties
이렇게 해 두면, 나중에 Kafka나 배치에서 로그를 처리할 때 훨씬 안정적으로 사용할 수 있습니다.
2.3 최소한의 데이터로 최대한의 정보 확보하기
처음에는 “이것도 넣을까, 저것도 넣을까” 하다 보면 이벤트 하나에 너무 많은 정보를 담게 됩니다. 하지만 무분별한 수집은 저장 비용 증가뿐 아니라, 오히려 데이터 품질 저하로 이어질 수 있습니다.
그래서 저희는 “지금 정말 필요한 필드만 수집하자” 는 원칙을 세웠습니다.
예를 들어 버튼 클릭 로그에서
- 버튼 이름
- 대상 상품 id
- 대상 태그
정도만 있으면 충분한데,
- 버튼 색상
- 버튼 위치 x/y
- 폰트 크기
같은 값까지 모두 저장하는 것은 현재 목적에는 과하다고 판단했습니다.
즉, 필요한 필드만, 분석 가능한 형태로 수집하는 것을 기본 원칙으로 삼았습니다.
3. 프론트와 로그 템플릿을 어떻게 맞췄는가
로그 설계는 백엔드만 정한다고 끝나는 것이 아니었습니다. 실제로 이벤트를 보내는 쪽은 프론트엔드이기 때문에, 프론트와 동일한 이벤트 템플릿을 공유하는 것이 매우 중요했습니다.
저희는 프론트와 행동 로그 설계를 엑셀로 먼저 맞춘 뒤, 이벤트별 JSON 템플릿을 고정하고 개발을 진행했습니다.
핵심은 다음과 같았습니다.
- 이벤트 이름을 고정한다.
- 공통 필드는 최대한 동일하게 간다.
- 이벤트별로 필요한
event_properties만 다르게 둔다. - 프론트와 백엔드가 동일한 명세를 본다.
이렇게 해야 로그를 나중에 적재하거나 분석할 때 필드 불일치 문제가 줄어들고, 프론트와 백엔드가 서로 다른 의미로 이벤트를 해석하는 문제도 줄일 수 있습니다.
4. 최종 공통 템플릿
저희가 최종적으로 프론트와 공유한 로그 공통 템플릿은 아래와 같습니다.
4.1 상품 상세 유형 클릭 (click_product_detail)
{
"event_id": 1000000000002,
"timestamp": "2026-03-05T12:02:00.000Z",
"event": "click",
"event_name": "click_product_detail",
"member_id": 1,
"event_properties": {
"page_url": "https://api.holliverse.site/api/v1/customer/plans/10",
"product_id": 10,
"product_name": "5G 요금제",
"product_type": "mobile",
"tags": ["영상OTT", "구독결제", "인기"]
}
}
이 이벤트는 사용자가 상품 상세 페이지를 눌렀을 때 발생합니다. 추천 시스템 관점에서는 특정 상품과 태그에 대한 직접 관심 신호로 볼 수 있습니다.
4.2 상품 리스트 유형 클릭 (click_list_type)
{
"event_id": 1000000000001,
"timestamp": "2026-03-05T12:01:00.000Z",
"event": "click",
"event_name": "click_list_type",
"event_properties": {
"page_url": "https://api.holliverse.site/api/v1/customer/plans?category=mobile",
"product_type": "mobile"
}
}
이 이벤트는 사용자가 특정 카테고리의 상품 리스트를 조회했을 때 발생합니다. 상품 개별 클릭보다는 약하지만, 어떤 상품 유형에 관심이 있는지를 판단하는 데 유용합니다.
4.3 상품 비교 버튼 클릭 (click_compare)
{
"event_id": 1000000000003,
"timestamp": "2026-03-05T12:03:00.000Z",
"event": "click",
"event_name": "click_compare",
"event_properties": {
"page_url": "https://api.holliverse.site/api/v1/customer/compare",
"target_id": 10,
"target_tags": ["영상OTT", "가성비", "가족결합"]
}
}
이 이벤트는 사용자가 상품 비교 기능을 눌렀을 때 발생합니다. 저희는 이 이벤트를 탐색 성향(explore) 과 변경 의도를 판단하는 중요한 행동 신호로 보았습니다.
4.4 요금제 변경 시도 (click_change)
변경 버튼을 눌렀을 때 발생하며, is_success가 false로 기록됩니다.
{
"event_id": 1000000000004,
"timestamp": "2026-03-05T12:04:00.000Z",
"event": "click",
"event_name": "click_change",
"event_properties": {
"page_url": "https://api.holliverse.site/api/v1/customer/plans?category=mobile",
"from_plan_id": 5,
"to_plan_id": 10,
"is_success": false
}
}
이 이벤트는 실제 변경 성공은 아니지만, 사용자가 변경을 시도했다는 강한 의도 신호입니다. 특히 이탈 위험군 또는 업셀 가능성 판정에 중요한 피처가 될 수 있다고 판단했습니다.
4.5 요금제 변경 성공 (click_change_success)
최종적으로 요금제 변경이 완료되었을 때 발생하며, is_success가 true로 기록됩니다.
{
"event_id": 1000000000005,
"timestamp": "2026-03-05T12:05:00.000Z",
"event": "click",
"event_name": "click_change_success",
"event_properties": {
"page_url": "https://api.holliverse.site/api/v1/customer/plans?category=mobile",
"from_plan_id": 5,
"to_plan_id": 10,
"is_success": true
}
}
이 이벤트는 탐색 단계가 아닌 실제 전환 결과를 나타냅니다. 분석 관점에서는 추천의 최종 성과 측정에도 연결될 수 있습니다.
4.6 쿠폰 클릭 (click_coupon)
혜택 페이지 등에서 쿠폰을 클릭하여 발급받거나 확인할 때의 로그입니다.
{
"event_id": 1000000000006,
"timestamp": "2026-03-05T12:06:00.000Z",
"event": "click",
"event_name": "click_coupon",
"event_properties": {
"page_url": "https://api.holliverse.site/api/v1/customer/benefits",
"coupon_name": "첫달 50% 할인 쿠폰"
}
}
이 이벤트는 비용 민감도, 혜택 추구 성향을 보여주는 지표로 활용할 수 있습니다.
다만 현재 문서 기준으로는 x 표시가 있으므로, 운영 여부는 별도로 정리할 필요가 있습니다.
4.7 위약금/패널티 관련 클릭 (click_penalty)
{
"event_id": 1000000000007,
"timestamp": "2026-03-05T12:07:00.000Z",
"event": "click",
"event_name": "click_penalty",
"event_properties": {
"page_url": "https://api.holliverse.site/api/v1/customer/mypage/penalty"
}
}
이 이벤트는 저희가 특히 중요하게 본 신호 중 하나입니다. 위약금 조회는 고객이 현재 상품 유지에 불안감을 느끼거나, 해지를 고민하고 있을 가능성을 보여주는 행동으로 해석될 수 있기 때문입니다.
따라서 이 이벤트는 추후 CHURN_RISK 세그먼트 판정에도 반영될 수 있도록 설계했습니다.
5. 왜 이런 이벤트를 골랐는가
수집 대상 이벤트를 정할 때, 저희는 단순 클릭 수를 늘리는 것이 아니라 추천과 고객 분류에 실제로 활용 가능한 이벤트만 남기려 했습니다.
최종적으로 선택한 이벤트들은 크게 세 가지 기준을 만족해야 했습니다.
5.1 고객의 관심사를 보여주는가
예를 들어
click_product_detailclick_list_typeclick_compare
는 고객이 어떤 상품을 궁금해하고, 어떤 카테고리에 관심을 갖고 있는지를 보여줍니다.
5.2 고객의 의도 변화를 보여주는가
예를 들어
click_changeclick_change_success
는 단순 관심을 넘어서, 실제로 상품 변경 의사가 있었는지를 보여줍니다.
5.3 고객의 위험 신호를 보여주는가
예를 들어
click_penalty- 최근 상담 데이터
는 해지 고민, 부담, 이탈 위험 같은 신호로 해석될 수 있습니다.
6. 여기까지의 의사결정 요약
이 단계에서 저희가 정리한 핵심 결정은 다음과 같습니다.
- 추천과 캐릭터 분류에 필요한 로그는 프론트에서 수집한다.
- 서버 로그는 최종 결과 확인용으로 의미가 있지만, 사용자 행동의 맥락을 보기에는 부족하다.
- 모든 이벤트는 공통 템플릿을 기반으로 설계한다.
- 이벤트 이름과 공통 필드는 최대한 고정한다.
- 분석과 추천에 실제로 활용 가능한 이벤트만 최소한으로 수집한다.
7. 다음 고민: 어떤 로그를 어디에 저장할 것인가
여기까지는 “어떤 로그를 어떻게 수집할 것인가”에 대한 이야기였습니다.
다음 단계에서는 자연스럽게 이런 질문으로 넘어가게 됩니다.
- 이렇게 수집한 로그를 어디에 저장할 것인가?
- 실시간 처리와 배치 처리를 어떻게 나눌 것인가?
- 최근 본 상품 로그는 어디에 적재할 것인가?
- Kafka, DB, S3/Data Lake 중 무엇을 어떤 용도로 쓸 것인가?
다음 섹션에서는 “어떤 로그를 어디에 저장할 것인가” 에 대한 고민과 결정 과정을 이어서 정리하겠습니다.

초기 아키텍쳐

변화된 아키텍쳐
