본문 바로가기

문돌이 존버/Django 스터디

Django DRF JWT를 이용한 회원가입/로그인 구현 - (1)

반응형

최근에 장고 대신 DRF(Django Rest Framework)를 사용해서 백엔드 구축 과제를 진행하고 있는데요. 이전에 데이터 처리 및 DB용으로 장고를 사용하긴 했어도, DRF는 처음이다보니 익숙치 않네요 ㅎㅎ 그래도 공식문서를 참고하고 열심히 구글링하며 하나씩 해보고 있답니다! 

이번 시간에는 DRF를 활용한 회원가입 및 로그인 API를 구현해보겠습니다. 이때 토큰(Token)은 JWT(Json Web Tokenization)를 사용할 것입니다. 이에 앞서 토큰의 개념에 대해서 알아봅시다.

토큰 기반 인증 시스템을 다른 말로 Stateless 서버라고 부릅니다. 반대되는 개념으로 Stateful 서버가 있는데, 이는 클라이언트에서 요청을 받을 때마다 클라이언트의 상태를 계속 유지하고, 서비스를 제공할 때 항상 참고해야 합니다.

예를 들어, 유저가 아이디와 비밀번호를 입력하고 로그인을 합니다. 이때 로그인이 성공하면 서버 내에 세션(session)을 만들게 되고, 세션 id, 유저 아이디, 비밀번호, 권한 등의 정보가 들어있습니다. 이후 서버는 세션 아이디인 쿠키(cookie)를 클라이언트에 보내고 클라이언트가 무언가 요청을 할 때마다 이 쿠키를 서버에 보내 서버가 확인하는 것이죠. 서버는 쿠키에 담긴 세션 id를 찾고, 유효시간 및 권한을 확인하여 요청에 맞는 특정 웹페이지를 보여주게 됩니다. 

이 세션을 저장할 때 서버 컴퓨터의 메모리(RAM)을 사용하기도, DB를 사용하기도 하는데 로그인 중인 유저 수가 늘어나게 되면 시스템이 과부화된다는 단점이 존재합니다. 

반대로 Stateless 서버는 유저의 상태 정보를 저장하지 않고 클라이언트 측에서 들어오는 요청만으로 작업을 수행합니다. 그렇다면 어떻게 유저 정보를 확인하고 그에 맞는 적절한 권한을 부여할 수 있을까요? 이때 사용하는 것이 바로 토큰입니다. 서버에서 토큰을 클라이언트에 전달하면, 클라이언트는 이를 저장해두고 요청을 할 때 해당 토큰을 함께 보내게 됩니다. 토큰을 보낼 때는 아래와 같이 HTTP request의 headers에 포함시키면 됩니다.

서버는 클라이언트가 보낸 토큰이 유효한지만 확인하고 적절한 권한을 부여하는 것이지, 더 이상 별도의 세션을 저장하고 비교하는 작업을 하지 않습니다. 어떤 토큰 형식을 사용할 것이냐에 대해선 JWT를 선택했고 간단하게 설명드리겠습니다. 왜 JWT를 사용했느냐 하는 문제에 대해선 다른 토큰 형식을 경험하지 않았기에 비교하여 설명드리긴 어려울 것 같습니다만... 1) 수많은 프로그래밍 언어에서 지원된다는 점, 2) 필요한 정보를 자체적으로 지니고 있다는 점, 3) 서버-클라이언트 간 쉽게 전달될 수 있다는 점이 장점으로 뽑힙니다. 

아래는 JWT 공식 홈페이지에 나온 설명입니다. JWT는 총 3가지로 구성되어 있는데요, HEADER / PAYLOAD / VERIFTY SIGNATURE 입니다. 

1. HEADER
  (1) alg: 어떤 해싱 알고리즘을 사용할 것인지
  (2) typ: 토큰 유형

2. PAYLOAD(토큰에 담을 정보, 정보의 한 조각을 클레임(claim)이라 부르며 name/value 한 쌍으로 이루어짐)
  (1) sub/name: 토큰 제목
  (2) iat: 토큰 발급 시간(issuaed_at)

  * 등록된 클레임
  (1) exp: 토큰 만료 시간(expiration)
  (2) aud: 토큰 대상자(audience)
  (3) iss: 토큰 발급자(issuer)

  * 비공개 클레임: 클라이언트와 서버 간 협의하에 사용되는 클레임

3. VERIFY SIGNATURE
  (1) your-256-bit-secret: header + payload 의 인코딩 값을 합친 후 주어진 시크릿 키로 해쉬 및 base64 인코딩을 통해 생성

주의!! HEADER와 PAYLOAD는 암호화(encryption)가 되지 않습니다. 따라서 PAYLOAD에 중요한 데이터 예를 들면, 카드번호나 계좌번호 같은 것은 입력하면 안됩니다. 

DRF에서 JWT를 사용하기 위해선 project 폴더 내 settings.py에 아래와 같이 내용을 추가해주어야 합니다. REST_FRAMEWORK 의 디폴트 권한 클래스를 JSONWebTokenAuthentication 으로 설정해주고, JWT_AUTH 를 보시면 여러가지 옵션을 선택할 수 있습니다. 

pip install djangorestframework djangorestframework-jwt
from datetime import timedelta

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.sites',
   
    'rest_framework',
]

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    ),
}

JWT_AUTH = {
    'JWT_ENCODE_HANDLER':
        'rest_framework_jwt.utils.jwt_encode_handler',
    'JWT_DECODE_HANDLER':
        'rest_framework_jwt.utils.jwt_decode_handler',
    'JWT_PAYLOAD_HANDLER':
        'rest_framework_jwt.utils.jwt_payload_handler',
    'JWT_PAYLOAD_GET_USER_ID_HANDLER':
        'rest_framework_jwt.utils.jwt_get_user_id_from_payload_handler',
    'JWT_RESPONSE_PAYLOAD_HANDLER':
        'rest_framework_jwt.utils.jwt_response_payload_handler',

    'JWT_SECRET_KEY': SECRET_KEY,
    'JWT_GET_USER_SECRET_KEY': None,
    'JWT_PUBLIC_KEY': None,
    'JWT_PRIVATE_KEY': None,
    'JWT_ALGORITHM': 'HS256',
    'JWT_VERIFY': True,
    'JWT_VERIFY_EXPIRATION': True,
    'JWT_LEEWAY': 0,
    'JWT_EXPIRATION_DELTA': timedelta(days=30),
    'JWT_AUDIENCE': None,
    'JWT_ISSUER': None,
    'JWT_ALLOW_REFRESH': False,
    'JWT_REFRESH_EXPIRATION_DELTA': timedelta(days=30),
    'JWT_AUTH_HEADER_PREFIX': 'JWT',
    'JWT_AUTH_COOKIE': None,
}

REST_USE_JWT = True

지금까지 JWT에 대한 기본적인 내용을 살펴봤습니다. 다음부터 본격적으로 DRF를 활용한 회원가입/로그인 API 구현을 살펴보겠습니다. Django DRF JWT를 이용한 회원가입/로그인 2편 보러가기

728x90
반응형