DB 없이 관리자용 방문자 통계를 만든 방법
사이트 운영을 시작하면 오늘 몇 명이 방문했는지와 누적 방문자가 궁금해진다. 처음부터 분석 플랫폼이나 데이터베이스를 붙이는 방법도 있지만, woosday에는 관리자만 확인하는 단순한 통계가 먼저 필요했다. 그래서 기존의 파일 기반 구조를 유지하면서 필요한 값만 저장했다.
먼저 방문자의 의미를 정했다
방문자 수는 요청 수와 다르다. 한 사람이 홈, 블로그, 프로젝트 페이지를 차례로 열어도 방문자 한 명으로 보고 싶었다. 반대로 쿠키를 삭제했거나 시크릿 모드를 사용하면 같은 사람을 다시 식별할 수 없다. 따라서 이 통계의 이름은 정확한 실명 사용자 수가 아니라 “익명 브라우저 기준 방문자 수”로 정했다.
페이지뷰나 요청 수를 그대로 세면 이미지와 CSS와 봇 요청까지 섞인다. 현재 필터는 공개 HTML 페이지의 정상 GET만 대상으로 하고, 관리자·지원 자료·디버거·정적 파일·헬스체크·사이트맵은 제외한다.
원본 IP를 저장하지 않는 식별 방식
첫 공개 페이지 방문 때 WOOSDAY_VISITOR_ID라는 무작위 UUID 쿠키를 발급한다. 서버에는 UUID 원문 대신 SHA-256 해시만 저장한다. 이 값은 로그인 계정이나 이름과 연결하지 않고, 방문 날짜별 중복을 줄이는 용도로만 사용한다.
쿠키를 사용하면 IP 주소를 장기간 보관하는 것보다 목적을 좁힐 수 있다. 그래도 브라우저에 식별자가 저장되고 서버에 해시가 남으므로 개인정보처리방침에 그 사실과 보유 기간을 적어야 한다. 통계를 추가하면서 개인정보 페이지도 함께 수정한 이유다.
저장 구조
운영 컨테이너는 루트 파일 시스템을 읽기 전용으로 실행한다. 통계 파일을 애플리케이션 이미지 안에 만들 수 없으므로 /data/analytics를 별도 Docker 볼륨에 연결했다.
/data/analytics/
├── known-visitors.txt
└── daily/
└── 2026-09-07.txt
known-visitors.txt에는 지금까지 본 해시를 한 줄씩 저장한다. 날짜 파일에는 해당 날짜에 본 해시를 저장한다. 같은 브라우저가 같은 날 여러 페이지를 열어도 Set에 한 번만 추가되므로 오늘 방문자 수는 중복되지 않는다. 컨테이너를 새로 만들더라도 볼륨은 유지되기 때문에 누적 수가 초기화되지 않는다.
정확하지 않은 경우
쿠키를 지우면 새 방문자로 집계된다. 여러 사람이 같은 브라우저 프로필을 공유하면 한 명으로 집계될 수 있다. 반대로 자동화된 브라우저가 쿠키를 저장하면 방문자로 포함될 수 있다. 이 통계는 결제나 보안 판단에 쓰는 데이터가 아니라 운영 방향을 잡기 위한 참고값이다.
방문자가 크게 늘면 파일을 계속 메모리에 읽어 두는 방식의 한계도 생긴다. 그때는 일별 집계만 보관하거나, 만료 정책을 두거나, 별도의 분석 저장소로 옮기는 결정을 할 수 있다. 작은 개인 사이트에서 처음부터 복잡한 파이프라인을 만들지 않은 이유는 실제 사용량을 확인한 뒤 확장하려 했기 때문이다.
관리자 화면에 노출하는 범위
통계는 /admin에서 인증된 관리자에게만 보여 준다. 공개 페이지에는 카운터를 넣지 않아 숫자를 조작하려는 요청을 유도하지 않고, 방문자의 브라우저에도 분석 결과를 표시하지 않는다. 관리자 비밀번호와 세션 보호가 약하면 통계의 저장 방식이 안전해도 의미가 없으므로 Spring Security와 CSRF 보호를 함께 유지한다.
운영 체크리스트
- 통계 볼륨을 Support·Debug 볼륨과 별도로 백업한다.
- 원본 IP나 User-Agent를 통계 파일에 추가하지 않는다.
- 쿠키 삭제와 시크릿 모드로 인한 중복 가능성을 관리자 화면에 안내한다.
- 파일이 손상되었을 때 애플리케이션 전체가 멈추지 않도록 복구 절차를 준비한다.
- 통계를 광고 성과나 사용자 식별 데이터로 과도하게 해석하지 않는다.
간단한 통계라도 무엇을 세는지와 무엇을 저장하지 않는지를 먼저 정하면 구현과 개인정보 안내가 함께 명확해진다. 현재 구조에서는 이 정도가 기능과 운영 복잡도의 균형점이다.
함께 읽기: