win-j의 자유로운 블로그

개발일지 - Djagno 방문자 수 집계하기

개발일지 · 2026.03.10 · 조회 3



네이버, 티스토리 블로그를 보면 방문자 수를 표시해 놓고 지금 이 블로그가 "유입이 얼마나 되는 블로그인가?" 라는 정보도 확인 할 수 있습니다.

그래서 이번에는 블로그에 방문자를 추적하고 카운트를 저장하는 기능을 추가했습니다.


1. 방문자수 집계를 위한 models.py 작성

from django.db import models
from django.utils import timezone

class VisitorLog(models.Model):
    ip_address = models.GenericIPAddressField()
    path = models.CharField(max_length=500, blank=True)
    user_agent = models.TextField(blank=True)
    referer = models.TextField(blank=True)
    visited_at = models.DateTimeField(auto_now_add=True)
    visit_date = models.DateField(auto_now_add=True)

    class Meta:
        ordering = ['-visited_at']

    def __str__(self):
        return f"{self.ip_address} - {self.visit_date}"


class DailyVisitor(models.Model):
    date = models.DateField(unique=True)
    count = models.PositiveIntegerField(default=0)

    class Meta:
        ordering = ['-date']

    def __str__(self):
        return f"{self.date} : {self.count}"


ip_address는 사용자의 IP주소를 저장하는 필드입니다.

models.GenericIPAddressFiled() 는 접속한 사용자의 IP주소를 저장 합니다. IPv4와 IPv6 를 지원하고 둘 다 저장 가능합니다.

저장된 IP 주소는 방문자 구분, 중복 방문 방지, 국가/지역 분석, 보안 로그로 활용이 가능합니다.


path는 사용자가 방문한 URL 경로를 저장하는 필드입니다.

저장은 / 메인페이지 /category/post 등등 현재 사용자가 어떤 URL로 방문을 했는지 저장하게 됩니다.


user_agent는 방문자가 어떤 브라우저/기기로 접속했는지 저장하는 필드입니다.

예를 들어 Mozilla/5.0 (Windows NT 10.0; Win64; x64) chrome/120.0.0.1 또는 Mozilla/5.0 (iPhone; CPU iPhone OS 17) Safari 로 표시되어

모바일 / PC 구분이 가능하고 브라우저 종류, OS 까지 구분이 가능합니다.


나중에 관리자 페이지를 만들고 방문자수 통계를 만들 때 모바일 이용자와 PC이용자를 구분할 수 있습니다.


referer 는 어디에서 들어왔는지 (유입 경로)를 저장하는 필드입니다.

예를 들어 http://google.com 또는 http://swpfun.titosry.com/123 등과 같이 어떤 링크를 통해서 접속했는지 구분이 가능해 나중 유입 분석 통계를 만들 때 사용합니다.


visited_at는 정확한 방문 시간을 저장 합니다. 데이터가 생성될 때 자동 기록 되고 표시는 2026-03-10 22:00:00으로 저장되어 시간 별 방문자 통계를 만들 때 사용합니다.


visit_date는 방문 날짜만 따로 저장하는 필드 입니다. visited_at에서 처럼 시간까지 표시가 되면 날짜별 통계를 계산할 때 쿼리 속도가 느려지기 때문에 방문 날짜만 따로 저장합니다.





사용자의 데이터를 저장했다면 이제 방문자수 통계를 만들 DailyVisitor 클래스를 만들었습니다.


여기에는 같은 날짜 데이터 1개만 허용하는 날짜만 저장하는 date 필드를 생성하고


PositiveIntegerField 0이상의 정수 기본값 0으로 하는 count 필드를 생성합니다. 여기에는 하루 동안 방문할 때마다 count 값이 증가합니다.



소스코드 완성 이후 makemigrations 와 migrate 를 통해 데이터베이스를 생성해야 합니다.


from django.utils import timezone
from .models import VisitorLog, DailyVisitor


def get_client_ip(request):
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        ip = x_forwarded_for.split(',')[0].strip()
    else:
        ip = request.META.get('REMOTE_ADDR')
    return ip or '0.0.0.0'


def count_visitor(request):
    ip = get_client_ip(request)
    today = timezone.localdate()

    session_key = f"visitor_counted_{today}"

    if not request.session.get(session_key):
        VisitorLog.objects.create(
            ip_address=ip,
            path=request.path,
            user_agent=request.META.get('HTTP_USER_AGENT', ''),
            referer=request.META.get('HTTP_REFERER', ''),
        )

        daily, _ = DailyVisitor.objects.get_or_create(date=today)
        daily.count += 1
        daily.save()

        request.session[session_key] = True


utils.py에 브라우저/세션 기준으로 하루 1회 방문자를 증가 시켜줍니다.


from .utils import count_visitor


class VisitorCountMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)

        excluded_prefixes = [
            '/admin/',
            '/static/',
            '/media/',
            '/favicon.ico',
        ]

        if request.path.startswith(tuple(excluded_prefixes)):
            return response

        count_visitor(request)
        return response


미들웨어에서 전체 방문자 자동 집계를 추가 해줍니다.





이후 settings.py에 미들웨어를 등록합니다.


MIDDLEWARE = [
    ...
    'blog.middleware.VisitorCountMiddleware',
]




from django.utils import timezone
from .models import DailyVisitor, Post


def blog_stats(request):
    today = timezone.localdate()

    today_visitors = DailyVisitor.objects.filter(date=today).first()
    today_count = today_visitors.count if today_visitors else 0

    total_visitors = DailyVisitor.objects.all().count()
    total_visit_count = sum(
        DailyVisitor.objects.values_list('count', flat=True)
    )

    return {
        'today_visitor_count': today_count,
        'total_visitor_count': total_visit_count,
    }


today_visitor_count 는 오늘 방문자

total_visitor_count는 전체 방문자



<div class="card mb-3">
  <div class="card-body">
    <h5 class="card-title">방문자 통계</h5>
    <p class="mb-1">오늘 방문자 : {{ today_visitor_count }}</p>
    <p class="mb-0">전체 방문자 : {{ total_visitor_count }}</p>
  </div>
</div>


이후 HTML 소스에 {{ today_visitor_count}}와 {{total_visitor_count}}를 추가 해 주면 오늘 방문자와 전체 방문자 수를 블로그 화면에 표시해 줄 수 있게됩니다.




그리고 이전에는 게시물을 클릭 할 때마다 조회수가 증가 되었는데 session을 활용해서 세션당 카운터 +1 이 되도록 수정했습니다.

def get_object(self, queryset=None):
    obj = super().get_object(queryset)

    session_key = f"viewed_post_{obj.pk}"
    if not self.request.session.get(session_key):
        Post.objects.filter(pk=obj.pk).update(views=F("views") + 1)
        self.request.session[session_key] = True
        obj.refresh_from_db(fields=["views"])

    return obj



이제 오늘 방문자 수와 전체 방문자 수를 집계 그리고 게시글의 조회수 카운터 까지 수정을 하게 되었습니다.


블로그 글은 작성이 가능해야 해서 글쓰기 기능만 만들어 둔 상태이고 썸네일 또한 글쓰기 이후 관리자 모드에서 수정을 통해 썸네일 등록 그리고 관리자 모드에서 글작성을 만든것이 아니라 관리자 모드에서는 글 내용 수정이 쉽지 않아...

글 수정기능 등도 만들어야 하는데.. 이건 시간이 많이 걸릴 듯 하고.. 여기에 새로운 관리자 페이지 까지 만들 예정이지만 다른 어떤 기능을 추가하게 될 수도 있겠네요