시작하기 전에


안녕하세요 똑똑한 개발자에서 백엔드 개발을 하고 있는 jujun입니다. 오늘은 Django 의 세션에 대해 알아보려합니다.

세션에 대해 막연하게만 알고 있어서 이번 기회에 보다 깊이 이해해 보려고 합니다.

.

본론


Session?

a session is a temporary and interactive information interchange between two or more communicating devices, or between a computer and user (see login session). An established communication session may involve more than one message in each direction. A session is typically stateful, meaning that at least one of the communicating parties needs to hold current state information and save information about the session history to be able to communicate, as opposed to stateless communication, where the communication consists of independent requests with responses.

위키디피아에 따르면 세션은 두개 이상의 기기들 간의 일시적이고 상호적인 정보교환 이라고 합니다. 세션은 특정한 시간에 생성되었다가 일정 시간이 지난 후 소멸합니다. 세션은 일반적으로 ‘상태’에 기반합니다. 서로 독립적인 request와 responses 를 교환하는 이전의 통신 법과 달리 통신 기기 들은 서로의 현재 ‘상태’ 를 저장하여 상대 기기와 통신합니다.

아하, 세션은 통신 할 때에 추가적인 데이터를 일시적으로 저장하는 것이군요.

Session in Django

session

위 사진은 장고의 모식도 입니다. request가 들어오면 우선 주황색 박스로 표시된 SessionMiddleware 가 url.py 이전에 다음 로직을 처리합니다. request에 있는 cookie 정보를 통해 저장되어 있는 session table 에서 해당 데이터를 가지고 view에서 해당 request를 다시 만납니다. 만약 cookie 정보가 없다면 새로운 세션을 만들어서 사용합니다. 아래는 SessionMiddleware의 소스코드 입니다.

class SessionMiddleware(MiddlewareMixin):
    # RemovedInDjango40Warning: when the deprecation ends, replace with:
    #   def __init__(self, get_response):
    def __init__(self, get_response=None):
        self._get_response_none_deprecation(get_response)
        self.get_response = get_response
        self._async_check()
        engine = import_module(settings.SESSION_ENGINE)
        self.SessionStore = engine.SessionStore

    def process_request(self, request):
        session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
        request.session = self.SessionStore(session_key)

    def process_response(self, request, response):
        """
        If request.session was modified, or if the configuration is to save the
        session every time, save the changes and set a session cookie or delete
        the session cookie if the session has been emptied.
        """
        try:
            accessed = request.session.accessed
            modified = request.session.modified
            empty = request.session.is_empty()
        except AttributeError:
            return response
        # First check if we need to delete this cookie.
        # The session should be deleted only if the session is entirely empty.
        if settings.SESSION_COOKIE_NAME in request.COOKIES and empty:
            response.delete_cookie(
                settings.SESSION_COOKIE_NAME,
                path=settings.SESSION_COOKIE_PATH,
                domain=settings.SESSION_COOKIE_DOMAIN,
                samesite=settings.SESSION_COOKIE_SAMESITE,
            )
            patch_vary_headers(response, ('Cookie',))
        else:
            if accessed:
                patch_vary_headers(response, ('Cookie',))
            if (modified or settings.SESSION_SAVE_EVERY_REQUEST) and not empty:
                if request.session.get_expire_at_browser_close():
                    max_age = None
                    expires = None
                else:
                    max_age = request.session.get_expiry_age()
                    expires_time = time.time() + max_age
                    expires = http_date(expires_time)
                # Save the session data and refresh the client cookie.
                # Skip session save for 500 responses, refs #3881.
                if response.status_code != 500:
                    try:
                        request.session.save()
                    except UpdateError:
                        raise SuspiciousOperation(
                            "The request's session was deleted before the "
                            "request completed. The user may have logged "
                            "out in a concurrent request, for example."
                        )
                    response.set_cookie(
                        settings.SESSION_COOKIE_NAME,
                        request.session.session_key, max_age=max_age,
                        expires=expires, domain=settings.SESSION_COOKIE_DOMAIN,
                        path=settings.SESSION_COOKIE_PATH,
                        secure=settings.SESSION_COOKIE_SECURE or None,
                        httponly=settings.SESSION_COOKIE_HTTPONLY or None,
                        samesite=settings.SESSION_COOKIE_SAMESITE,
                    )
        return response

settings 기본 값 확인하기

. .

더 알아보기

SessionMiddleware는 서버로 들어오는 모든 브라우저 마다 session_key를 제공해서 서로를 식별합니다. 그리고 session 은 딕셔너리 형태로 database에 저장됩니다 .

sqlite> .tables
auth_group                  logger_emaillog           
auth_group_permissions      logger_phonelog           
auth_permission             logger_pushlog            
chat_chat                   logger_tasklog            
chat_chat_user_set          raw_controlpanel          
chat_chatimage              raw_country               
chat_message                raw_party                 
django_admin_log            raw_regionsub             
django_content_type         raw_regionupp             
django_migrations           result_paint 
django_session              result_resultcountry  

어떤 프로젝트의 데이터 베이스의 목록입니다. 세션 정보는 django_session 테이블에 저장됩니다. .

sqlite> .schema django_session
CREATE TABLE IF NOT EXISTS "django_session" ("session_key" varchar(40) NOT NULL PRIMARY KEY, "session_data" text NOT NULL, "expire_date" datetime NOT NULL);
CREATE INDEX "django_session_expire_date_a5c62663" ON "django_session" ("expire_date");

테이블의 형식입니다. session_key와 session_data, expire_date 컬럼이 있습니다. session_key는 긴 문자열로 각 리퀘스트를 식별하는데 사용합니다. .

sqlite> select * from django_session;
kxjq1aybqszbb8qz75kq5k1xlhracgkn|.eJxVjDEOAiEURO9CbYjAh79Y2u8ZCPBBVg0ky25lvLuSbKHNFPPezIs5v2_F7T2tbiF2YYKdfrvg4yPVAeju663x2Oq2LoEPhR-087lRel4P9--g-F7G2iKQQAshGZCUMQtNZISkQFpB1gheaW_MFCOgwJDV-Zs62jQZCcDeH-FZN4U:1lq42T:b4yttV2s8s0sEzkYLRil09TeBfUJFuehTG3vVr_xxmA|2021-06-21 10:26:49.780397
xjg7ck7fap165bc3pfl9lprwesluyl5d|.eJxVjEsOwjAMBe-SNYpqO3UCS_Y9Q-XECS2gVOpnhbg7VOoCtm9m3sv0sq1Dvy157kc1FwPm9LtFSY9cd6B3qbfJpqmu8xjtrtiDLrabND-vh_t3MMgyfGvitiUpGtGrYipUfAPMAV2Tc4DAgg6kJY18FkrIERIDIRVwIXlv3h_WVjc2:1lqQM9:bYkYGX80hC19FItTdpS2mHxZx7j6753JEkVO1O-WkNQ|2021-06-22 10:16:37.501155
hk60yyp4nlid7pmj07e6qekqofkufhsp|.eJxVjEsOwjAMBe-SNYpqO3UCS_Y9Q-XECS2gVOpnhbg7VOoCtm9m3sv0sq1Dvy157kc1FwPm9LtFSY9cd6B3qbfJpqmu8xjtrtiDLrabND-vh_t3MMgyfGvitiUpGtGrYipUfAPMAV2Tc4DAgg6kJY18FkrIERIDIRVwIXlv3h_WVjc2:1lqmwe:LCKes9iJmTp9YVHm1csf5MVlSIeFmNSgU7HzbJchTcY|2021-06-23 10:23:48.998589
x490m6eyi15giwxg0ynsr3i0grx1hqx5|.eJxVjEsOwjAMBe-SNYpqO3UCS_Y9Q-XECS2gVOpnhbg7VOoCtm9m3sv0sq1Dvy157kc1FwPm9LtFSY9cd6B3qbfJpqmu8xjtrtiDLrabND-vh_t3MMgyfGvitiUpGtGrYipUfAPMAV2Tc4DAgg6kJY18FkrIERIDIRVwIXlv3h_WVjc2:1lrEGk:EP0bnsnh1JcVDz05eTyHLqUhh2ep-P9ucmSbsvx2QZo|2021-06-24 15:34:22.082250
854fapp4qepk3tmfz86bvx8b58sbc12n|.eJxVjEsOwjAMBe-SNYpqO3UCS_Y9Q-XECS2gVOpnhbg7VOoCtm9m3sv0sq1Dvy157kc1FwPm9LtFSY9cd6B3qbfJpqmu8xjtrtiDLrabND-vh_t3MMgyfGvitiUpGtGrYipUfAPMAV2Tc4DAgg6kJY18FkrIERIDIRVwIXlv3h_WVjc2:1lrGDN:7tL9SkVC3A_aOe_rx6a_salvEEBW-451dDgJQxHmJuI|2021-06-24 17:39:01.436472
ythw5vob5dykrktggzo06oz97toqy5sd|.eJxVjEsOwjAMBe-SNYpqO3UCS_Y9Q-XECS2gVOpnhbg7VOoCtm9m3sv0sq1Dvy157kc1FwPm9LtFSY9cd6B3qbfJpqmu8xjtrtiDLrabND-vh_t3MMgyfGvitiUpGtGrYipUfAPMAV2Tc4DAgg6kJY18FkrIERIDIRVwIXlv3h_WVjc2:1lrVlj:_zaqOOrWX-1-4oqeA6hZ4fYUXCUdlW5WKO3IJUVfF38|2021-06-25 10:15:31.721094

테이블 내부를 보았습니다. data 값에 알 수 없 는 데이터가 들어있습니다.

$ python manage.py shell 
>>> from django.contrib.sessions.models import Session
>>> session = Session.objects.get(session_key='ythw5vob5dykrktggzo06oz97toqy5sd')
(0.000) SELECT "django_session"."session_key", "django_session"."session_data", "django_session"."expire_date" FROM "django_session" WHERE "django_session"."session_key" = 'ythw5vob5dykrktggzo06oz97toqy5sd' LIMIT 21; args=('ythw5vob5dykrktggzo06oz97toqy5sd',)
>>> session.get_decoded()
{'_auth_user_id': '1', '_auth_user_backend': 'django.contrib.auth.backends.ModelBackend', '_auth_user_hash': '36553afdb27dd2cf3f701668240ee8186a241a53db69a3c26b1c61323f148c77'}

세션 테이블의 가장 마지막에 있는 데이터를 보았습니다.

마치는 말


지금까지 장고 세션에 대해 더 자세히 알아보는 시간을 가졌습니다. 세션이 무엇이고 장고에서 어떻게 사용할 수 있는지 확인해봤습니다. 막연하게만 알고 있던 세션에 대해 보다 명확하게 알게되어 상당히 만족스럽습니다. 여러분도 그런 시간이었기를 바랍니다.

jujun's profile image

jujun

2021-06-16 10:35

Read more posts by this author