IP 해시 기반 방문자 트래킹으로 엔트리 포스트 구조 설계 가이드
IP 해시로 하루 단위 방문자를 정의하고, 첫 유입 페이지를 함께 저장하는 VisitorStorage 설계를 소개합니다. 간단한 구조로도 관리용 대시보드에 쓸만한 데이터를 모으는 방법을 다룹니다.
블로그 운영을 하다 보면 “오늘 실제 방문자 수(UU)는 대략 몇 명이지?”,
“어떤 글이 입구(엔트리 포스트) 역할을 하고 있지?” 같은 질문이 생깁니다.
보통은 Google Analytics 같은 외부 도구를 붙이면 해결되지만,
저는 이 실험을 블록체인 컨트랙트(온체인) 에 저장하는 방식으로 설계했습니다.
이 글에서는
- IP 기반 방문자 정의를 어떻게 했는지
- IP를 그대로 저장하지 않고 해시로 처리한 이유
- 온체인에 저장할 때 어떤 구조로 하면 가스/조회가 현실적인지
- “정확한 사용자 수”가 아니라 “운영 지표”로서 어떤 의미가 있는지
를 정리합니다.
![]()
TL;DR
- 방문자 정의는 “IP 해시 + 날짜”로 단순화합니다. (같은 날 같은 IP는 1명)
- 원문 IP는 저장하지 않고, pepper(서버 비밀값) + 날짜를 섞어 해시합니다.
- 온체인에는 “모든 로그”를 쌓기보다, 일일 집계(카운트) 중심으로 저장해야 현실적입니다.
- 엔트리 포스트 Top N 같은 분석은 컨트랙트만으로 한계가 있으므로, 이벤트(Event) + 오프체인 인덱싱을 함께 쓰는 설계가 가장 실용적입니다.
방문자 트래킹이 필요한 순간
블로그를 운영하다 보면 단순한 PV(페이지뷰)로는 부족해집니다.
- 오늘 “대략 몇 명”이 왔는가?
- 어떤 글이 입구(엔트리) 역할을 하는가?
- 시리즈 글 중 어디가 유입을 만들고 있는가?
여기서 중요한 건 “정확한 사용자 수”가 아니라,
운영 의사결정에 쓸 수 있는 방향성 지표입니다.
IP 기준 방문자 정의하기
방문자 기준은 보통 아래 중 하나입니다.
| 기준 | 장점 | 한계 |
|---|---|---|
| IP 주소 | 구현이 단순, 서버에서 바로 사용 | 공유 IP/VPN/프록시로 여러 사람이 섞임 |
| 쿠키/로컬스토리지 | 브라우저 단위로 비교적 정확 | 쿠키 삭제/브라우저 변경 |
| 로그인 사용자 | 가장 명확 | 로그인 없는 블로그에 부적합 |
저는 “개인 블로그 + 간단한 운영 지표”가 목적이라
IP 기반 + 하루 단위로 방문자를 정의했습니다.
즉:
- 같은 날 같은 IP → 1명
- 날짜가 바뀌면 다시 1명으로 카운팅
(중요) Vercel 환경에서 IP를 얻는 방법
Vercel 같은 프록시/서버리스 환경에서는 클라이언트 IP가 보통 헤더로 전달됩니다.
권장 규칙:
x-forwarded-for가 있으면 맨 앞 IP를 사용(원본 후보)- 없으면
x-real-ip등 대체 헤더 시도 - 그래도 없으면 집계에서 제외하거나 unknown 처리
IPv6, 프록시 체인(콤마로 여러 IP)이 흔하므로
IP 파싱 로직은 한 번 테스트해두는 편이 좋습니다.
IP를 그대로 저장하지 않는 이유(해시)
원문 IP를 그대로 저장하는 것은 개인정보 리스크가 큽니다.
그래서 저는 IP 자체를 저장하지 않고 “같은 방문자인지 판단 가능한 형태”로만 남기기 위해 해시를 사용합니다.
하지만 단순히 SHA256(ip)만 해도 다음 문제가 있습니다.
- 같은 IP는 항상 같은 해시 → 장기 추적 가능성
- 특정 환경에선 추정 위험(사전 대입)이 완전히 0이 아님
그래서 “하루 단위 방문자” 정의에 맞춰 아래처럼 만듭니다.
권장안: pepper(서버 비밀값) + date를 섞어 하루 단위 해시
ipHash = SHA256( ip + pepper + YYYY-MM-DD )
이렇게 하면 같은 IP라도 날짜가 바뀌면 해시가 바뀌고,
“IP+하루”라는 방문자 정의에도 정확히 부합합니다.
pepper는 서버 환경 변수로만 관리하고, 절대 클라이언트로 내려가지 않게 합니다.
온체인 저장에서 가장 중요한 현실: “어떻게 저장할 것인가”
여기서부터가 핵심입니다.
온체인에 방문 로그를 “그대로 다” 저장하면
가스비가 빠르게 커지고, 데이터가 쌓일수록 조회도 어려워집니다.
그래서 온체인 저장은 크게 두 방향 중 하나로 나뉩니다.
방향 A) 온체인에 “일일 집계값”만 저장 (추천)
YYYY-MM-DD별로 오늘 방문자 수를 저장- 엔트리 포스트 등 세부 분석은 이벤트/오프체인에서 처리
장점:
- 가스 비용을 예측 가능하게 낮출 수 있음
- 컨트랙트가 “가벼운 통계 저장소”로 동작
단점:
- 컨트랙트만으로 Top N 같은 분석은 제한적
방향 B) 온체인에 “로그(경로까지)”를 모두 저장
- 방문자마다 path까지 컨트랙트 스토리지에 저장
장점:
- 온체인만으로도 많은 정보 보존
단점:
- 가스/스토리지 비용 급증
- 장기 운영이 현실적으로 어려워질 가능성 큼
저는 개인 프로젝트 운영 관점에서
방향 A(집계 중심) + “필요한 부분은 이벤트로 남기기”를 선택했습니다.
추천하는 컨트랙트 설계(집계 중심 + 이벤트)
온체인에는 최소한 아래 2가지만 남겨도 운영 지표로 충분합니다.
dailyUniqueCount[dateKey]: 일일 유니크 방문자 수entryCount[dateKey][pathKey]: 엔트리 포스트별 카운트(선택)
여기서 핵심은 “유니크 방문자 체크”입니다.
동일한 방문자가 하루에 여러 번 들어와도 1번만 카운팅해야 합니다.
그래서 서버는 요청을 받을 때마다:
dateKey = YYYY-MM-DD를 정수로 인코딩하거나(예: 20260121)visitorKey = ipHash를bytes32로 만들고- 컨트랙트에
markVisited(dateKey, visitorKey, pathKey)를 호출합니다.
컨트랙트는 내부에서:
visited[dateKey][visitorKey]가 이미 true면 카운트하지 않고 종료- false면 true로 바꾸고
dailyUniqueCount[dateKey]++- (선택)
entryCount[dateKey][pathKey]++ - 이벤트를 emit
path는 문자열을 그대로 넣기보다
pathKey = keccak256(bytes(path))같은 형태로 키만 저장하는 것이 현실적입니다.
왜 이벤트(Event)를 같이 쓰는가?
“온체인에 저장된 값”은 신뢰성이 있지만,
그 자체로 “분석”을 하기엔 불편합니다.
예를 들어 “엔트리 Top 10”을 컨트랙트만으로 뽑으려면:
- 모든 pathKey를 순회해야 하는데, Solidity는 이런 반복 조회가 어렵습니다.
그래서 실무에서는 보통:
- 컨트랙트는 정합성 있는 최소 상태만 저장하고
- 세부 분석은 이벤트를 인덱싱해서 오프체인에서 계산합니다.
이렇게 하면:
- 온체인은 가볍게 유지되고
- 대시보드/통계는 빠르게 확장 가능합니다.
운영에서 꼭 방어해야 하는 3가지
1) 봇/프리페치
봇이 계속 hit하면 온체인 저장 자체가 비용이 됩니다.
최소한 다음 중 하나를 합니다.
- 특정 UA 차단
- rate limit (IP 기준 분당 N회)
- 특정 경로(정적 파일) 제외
2) 관리자/내부 트래픽 제외
운영자 본인 접속은 지표를 망가뜨립니다.
- 관리자 쿠키/토큰이 있으면 제외
- 특정 IP(사무실/집) 제외(가능하면)
3) 개인정보 리스크
- 원문 IP 저장 금지
- pepper는 서버에서만
- 보관 목적을 명확히 하고, 필요 이상으로 세부 정보를 저장하지 않기
이 방식의 한계(정확한 사용자 수가 아니다)
IP 기반 UU는 어디까지나 근사치입니다.
- 공유 IP: 여러 사람이 1명으로 합쳐짐
- VPN: 같은 사람이 여러 IP로 보일 수 있음
그래도 이 방식은 “운영 지표”로 의미가 있습니다.
- 어떤 글이 입구 역할을 하는지
- 오늘/이번 주 흐름이 어떤지
결론
온체인에 방문자 트래킹을 저장하는 건 “정답”이라기보다
실험 목적 + 설계 학습 목적으로 매우 좋은 주제라고 생각합니다.
핵심은:
- 온체인에는 “집계 중심”으로 최소화하고
- 디테일 분석은 이벤트 + 오프체인 인덱싱으로 확장하며
- 개인정보/봇/비용을 반드시 같이 관리하는 것
입니다.