×

TODAY   22    TOTAL   2430   

 

Orca the Whale

Django Custom Middleware를 통해 방문자 집계 기능 구현하기 Django

조회수 : 78

작성일 : 2019-01-28 23:04

django middleware python

1. 개요

나는 블로그를 단순히 내가 공부한 게시글을 올리고 댓글을 읽는 것으로 생각하지 않는다.

티스토리와 같은 플랫폼에서 만들어진 블로그가 아닌 내가 직접 만든 블로그이기에,

내가 만들고 싶은 모든 것들을 해볼 수 있는 공간이라고 생각한다.

반대로 말하면 모든 잡다한 기능까지 직접 만들어야 한다는 단점이 생기지만 난 단점보단 장점에 초점을 맞췄다.

그리고 그 기능 중 하나가 '방문자 수 집계' 다. 

어떻게 보면 가장 중요한 기능이기도 하다.

google analytics를 통해 방문자 수 집계를 할 수 있지만

난 실제 값의 정확성보다 이 개발을 통해 얻는 것에 의의를 두었고, 내가 집계한 통계들로 더 많은 것을 생각하고 있기에 직접 구현해봤다.



2. 설계

특별한 건 없었다. 얼마 전에 만들었던 게시글의 조회수를 집계하는 기능과 크게 다를게 없었다.

차이점은 각각의 게시글마다 통계를 낸다는 것과 모든 GET 요청에서 통계를 낸다는 것이다.

난관에 봉착했다. 

게시글 조회수는 게시글을 보여주는 뷰에서 통계를 내면 그만이지만, 모든 서비스에서의 GET 요청은 어떻게 처리하지? 라는 의문이 생겼다.

모든 뷰에서 방문자를 집계하는 함수를 호출해야 하나?

login_required처럼?

난 정답을 미들웨어에서 찾았다.



3. What is Django Middleware?

미들웨어란 무엇일까?

위키백과에서는 미들웨어를 다음과 같이 정의하고 있다.

미들웨어는 양 쪽을 연결하여 데이터를 주고 받을  있도록 중간에서 매개 역할을 하는 소프트웨어, 네트워크를 통해서 연결된 여러 개의 컴퓨터에 있는 많은 프로세스들에게 어떤 서비스를 사용할 수 있도록 연결해 주는 소프트웨어를 말한다.

장고 공식 문서에서는 미들웨어를 다음과 같이 정의하고 있다.

미들웨어는 장고의 요청/응답 프로세싱의 중간에서 작업을 하는 가볍고 로우-레벨인 프레임워크이다. 각각의 미들웨어는 각자의 특별한 역할을 수행한다.

그렇다. 그 미들웨어가 이 미들웨어이다.

직접 만들기 전에 미들웨어에 대해 더 알아보자.



3. Middleware Example

각각의 미들웨어는 get_response라는 호출 가능한 함수를 전달받고, 미들웨어를 반환한다.

마치 뷰처럼 요청을 받고 응답을 반환하는 것이다.

미들웨어도 뷰처럼 함수형과 클래스형이 있는데, 난 클래스형만 다루겠다.

다음은 장고 공식 문서에서 제공하는 미들웨어 예시이다.

class SimpleMiddleware(object):
    def __init__(self, get_response):
        self.get_response = get_response
        # One-time configuration and initialization.

    def __call__(self, request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.

        response = self.get_response(request)

        # Code to be executed for each request/response after
        # the view is called.

        return response

후술하겠지만 미들웨어는 순차적으로 호출된다.

현재 미들웨어가 선언된 미들웨어의 가장 마지막이라면 get_response는 실제 뷰가 되거나 , 다음 미들웨어로 연결하는 고리가 된다.



4. __init(get_response)__

미들웨어는 get_response매개 변수를 필수로 전달받아야 한다.

하지만 그 과정에서 주의할 점이 두 가지가 있다.

 4-1. 미들웨어는 get_reponse만을 받는다. 생성자를 통해 다른 매개 변수를 받을 수 없다.

 4-2__call__ 함수와는 다르게 한 번의 요청마다 생성자가 호출되지 않고 웹 서버가 시작될 때 단 한 번 호출된다.



5. 미들웨어 활성화

미들웨어를 활성화 시키기 위해서는 settings에 있는 MIDDLEWARE 리스트에 미들웨어를 추가해야 한다.

각각의 미들웨어는 전체 경로와 클래스, 혹은 함수의 이름으로 표시될 수 있는데, MIDDLEWARE에 이를 추가해주면 된다.

ex) <app 이름>.<확장자를 제외한 파이썬 파일 이름>.<클래스 이름>


장고는 미들웨어가 필수가 아니기에 MIDDLEWARE 리스트를 비워둘 수 있다.


6. 미들웨어의 순서와 계층화

미들웨어는 MIDDLEWARE 리스트에 선언된 순서대로 작동한다. 

미들웨어의 순서가 중요한 이유는 한 미들웨어가 다른 미들웨어의 영향을 받거나 의존 관계일 수 있기 때문이다.

양파다.

가장 안 쪽에 있는 속을 여러 겹이 감싸고 있는 것이 양파가 미들웨어와 뷰처럼 생겼다.

요청이 뷰에 도달하려면 그 전에 미들웨어를 순서대로 거친다. 

이때 각 미들웨어에서 다음 미들웨어로 요청을 넘길 때 get_response를 호출한다.

뷰에 도달하고 나서 응답도 반대 방향으로 똑같이 나가게 된다.

만약 한 미들웨어에서 요청을 넘기지 않고 끊는다면,

그대로 응답이 순서대로 반환되고 뷰를 포함한 그 다음 순서의 미들웨어들은 요청을 처리하지 않는다.



7. 구현

기본적인 내용은 모두 파악했으니 한번 구현해보자.

statis라는 앱에 middleware.py라는 파일을 생성했다.

장고 공식 문서의 예시를 살짝 변환하여 미들웨어를 구현했다.


class VisitorCountMiddleware(object):
    def __init__(self, get_response):
        self.get_response = get_response
       
    def __call__(self, request):              response = self.get_response(request)         # GET 요청인 경우
        if request.method == 'GET':             # ip별로 방문한 기록을 통해 방문자 통계를 구한다.

        return response


이미 저번 포스팅에서 비슷한 내용을 다루었기에 자세한 코드는 생략하겠다.

사이드바를 열어 내 깃허브에 접속하면 코드를 확인 할 수 있다.

이제 미들웨어를 활성화 시키기 위해 MIDDLEWARE에 추가해주자.

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'statis.middleware.VisitorCountMiddleware',
]
서버를 구동시키면 미들웨어가 제대로 작동하는 것을 볼 수 있다.



8. 여담

- 양파는 맛있다.

https://docs.djangoproject.com/ko/1.11/topics/http/middleware/ 에서 더 많은 내용을 확인 할 수 있다.


There are 1 comments.

김씨의 기준 많은 도움이 되었습니다.    2019-01-29 17:57 답글 삭제
  • 김씨의 기준 답글 삭제 많은 도움이 되었습니다. 2019-01-29 17:57 |