웹 애플리케이션 상태 관리를 위한 Cookie와 Session 활용 전략

사용자 추적의 필요성과 HTTP의 한계

웹 서비스가 사용자의 이전 활동이나 신원을 기억하지 못한다면, 매번 로그인을 다시 하거나 장바구니가 초기화되는 등 심각한 불편을 초래하게 됩니다. 이러한 기능을 전문 용어로 사용자 추적(User Tracking)이라고 합니다. 서버 관점에서 사용자를 식별하는 가장 직관적인 방법은 사용자별 정보를 담은 객체를 생성하는 것이며, 이를 **세션(Session)**이라고 부릅니다. 하지만 웹의 기반이 되는 HTTP 프로토콜은 두 가지 큰 특징을 가집니다.
  • 비연결성(Connectionless): 클라이언트의 요청에 응답한 후 즉시 연결을 끊습니다.
  • 무상태(Stateless): 이전 요청과 현재 요청 사이의 연관성을 알지 못합니다.
이러한 한계를 극복하기 위해 서버는 각 세션에 고유한 식별자인 세션 ID(Session ID)를 부여합니다. 클라이언트가 요청 시 이 식별자를 함께 전달하면, 서버는 이를 통해 저장된 세션 객체를 찾아 사용자를 식별합니다.

세션 식별자 전달 방식

클라이언트가 세션 ID를 유지하고 서버로 재전송하는 방법은 크게 세 가지가 있습니다.
  1. URL 리라이팅: URL 파라미터에 세션 ID를 포함시키는 방식입니다. (예: /main?sid=abc123)
  2. 숨겨진 폼 필드(Hidden Field): HTML 폼 전송 시 <input type="hidden"> 태그를 사용하여 보이지 않게 데이터를 전달합니다.
  3. 브라우저 저장소: 가장 보편적인 방식으로 쿠키(Cookie)를 사용합니다. 쿠키는 브라우저에 저장되는 키-값 쌍의 작은 데이터 조각이며, 매 요청마다 HTTP 헤더에 포함되어 자동으로 서버에 전달됩니다. 최신 브라우저에서는 쿠키 외에도 LocalStorage, SessionStorage 등을 지원합니다.

Django에서의 세션 관리

Django 프레임워크는 settings.pySessionMiddleware를 통해 세션 기능을 기본 제공합니다. 이 미들웨어 덕분에 개발자는 HttpRequest 객체의 session 속성을 딕셔너리처럼 자유롭게 사용할 수 있습니다. Django는 기본적으로 세션 데이터를 데이터베이스에 JSON 형식으로 직렬화하여 저장합니다. 이는 이전의 Pickle 방식보다 보안상 안전합니다. 다만, 대규모 트래픽 환경에서는 데이터베이스 부하를 줄이기 위해 세션을 캐시(Redis 등)에 저장하는 설정이 권장되기도 합니다.

코드 예시: 인증 코드 확인 및 로그인 처리

다음은 세션을 활용하여 보안 인증 코드를 관리하고 로그인을 처리하는 로직을 재구성한 예시입니다.
def generate_verify_code(req):
    """인증 코드 생성 및 세션 저장"""
    code_text = create_random_string()
    # 세션에 인증 코드 저장
    req.session['verify_code'] = code_text
    image = CodeImageGenerator.generate(code_text)
    return HttpResponse(image, content_type='image/png')

def user_sign_in(req):
    """로그인 처리 로직"""
    error_msg = ''
    if req.method == 'POST':
        form = SignInForm(req.POST)
        if form.is_valid():
            input_code = form.cleaned_data['verify_code']
            stored_code = req.session.get('verify_code', '')
            
            # 대소문자 구분 없이 코드 비교
            if stored_code.lower() != input_code.lower():
                error_msg = '인증 코드가 일치하지 않습니다.'
            else:
                uid = form.cleaned_data['username']
                upw = form.cleaned_data['password']
                member = Member.objects.filter(username=uid, password=upw).first()
                if member:
                    # 세션에 사용자 정보 기록
                    req.session['member_pk'] = member.id
                    req.session['member_name'] = member.username
                    return redirect('/dashboard/')
                else:
                    error_msg = '계정 정보가 올바르지 않습니다.'
    return render(req, 'signin.html', {'error': error_msg})

코드 예시: 로그아웃 처리

세션을 안전하게 종료하려면 서버측 데이터와 클라이언트측 쿠키를 모두 정리해야 합니다.
def user_sign_out(req):
    """세션 만료 및 로그아웃"""
    req.session.flush()
    return redirect('/login/')

데이터베이스 세션 구조 및 설정

Django의 세션 데이터는 django_session 테이블에 저장되며 구조는 다음과 같습니다.
session_key session_data expire_date
abc123uniqueid Base64로 인코딩된 데이터... 2023-12-31 23:59:59
만료된 세션 데이터를 정리하려면 다음 명령어를 사용합니다.
python manage.py clearsessions

세션 환경 설정 (settings.py)

주요 보안 및 유지 시간 설정은 다음과 같습니다.
# 세션 유지 시간 (초 단위, 예: 1일)
SESSION_COOKIE_AGE = 86400

# 브라우저 종료 시 세션 만료 여부
SESSION_EXPIRE_AT_BROWSER_CLOSE = True

# 세션 저장소 엔진 설정 (캐시 기반)
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'

쿠키 직접 제어하기

Django에서는 세션 외에도 쿠키를 직접 읽고 쓸 수 있는 인터페이스를 제공합니다.
  • 쿠키 쓰기: HttpResponse.set_cookie() 또는 보안이 강화된 set_signed_cookie()를 사용합니다. 서명된 쿠키는 SECRET_KEY를 이용해 데이터 변조 여부를 확인합니다.
  • 쿠키 읽기: HttpRequest.COOKIES 속성을 통해 접근합니다.

Cookie의 대안: Web Storage

쿠키는 매 요청마다 서버로 전송되므로 데이터 크기가 크면 네트워크 성능에 영향을 줄 수 있습니다. HTML5에서는 이를 보완하기 위해 두 가지 저장 방식을 제안합니다.
  • LocalStorage: 브라우저를 닫아도 데이터가 영구적으로 유지됩니다.
  • SessionStorage: 탭이나 브라우저 창을 닫으면 데이터가 소멸됩니다.
보안이 중요한 민감 정보는 서버측 세션에 두되, 단순 UI 상태 값 등은 이러한 로컬 저장소를 활용하는 것이 효율적입니다.

태그: Django sessions cookies web-security backend

7월 21일 08:16에 게시됨