시작하기 전에
안녕하세요 똑똑한 개발자에서 백엔드 개발을 하고 있는 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
위 사진은 장고의 모식도 입니다. 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
. .
더 알아보기
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'}
세션 테이블의 가장 마지막에 있는 데이터를 보았습니다.
마치는 말
지금까지 장고 세션에 대해 더 자세히 알아보는 시간을 가졌습니다. 세션이 무엇이고 장고에서 어떻게 사용할 수 있는지 확인해봤습니다. 막연하게만 알고 있던 세션에 대해 보다 명확하게 알게되어 상당히 만족스럽습니다. 여러분도 그런 시간이었기를 바랍니다.