
Django ๋ ์ ๋ง ๋ง์ ๊ธฐ๋ฅ๋ค์ด ๋ฏธ๋ฆฌ ์ ์๋์ด์๋ ํ๋ ์์ํฌ๋ค
๊ทธ ์ค Django๋ก RESTful API๋ฅผ ์์ฑํ๊ธฐ ์ํด์(์ ๋ง ์ด๋ ค์ด RESTfulโฆ โน๏ธ) Django REST framwork๋ฅผ ์ฌ์ฉํด ์ฌ์ฉ์ ๊ธฐ๋ฅ์ ๋ง๋ค์ด๋ณด๋ ค๊ณ ํ๋ค
๊ฐ๋ฐํ๊ฒฝ ์ค๋น
๊ฐ๋ฐ์ ์งํํ๊ธฐ ์ํ ํ๋ ์์ํฌ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ค์ ๋ฏธ๋ฆฌ ์ค์น ํด ์ฃผ์
- django
- django-restframework
- django-restframework-simplejwt
Python์ ์ฌ์ฉ ํด ๊ฐ๋ฐ ํ ๋,
Poetry
๋ฅผ ์ฌ์ฉํ๋ฉด Dependency๋ค์ ์ ์ฒดํฌํด์ฃผ์ด ๊ฐ๋ฐ์ด ํธํ๋คPoetry
๋ mac์ ์ฌ์ฉ ํ๋ค๋ฉด brew๋ก๋ ์ค์น๊ฐ ๊ฐ๋ฅํ๊ณ , pip์ ํตํ ์ค์น๋ ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ ํ๋ฒ ์ฝ์ด๋ณด๋ฉด ์ข์ ๊ฒ ๊ฐ๋ค.Poetry
reference - https://python-poetry.org/
Install Poetry
brew install poetry
Create VirtualEnv (with Poetry)
poetry init
- ์์ ๋ช ๋ น์ด๋ฅผ ์ฌ์ฉํ๋ฉด poetry์ ๊ด๋ จ๋ ํ์ผ์ด ์๊ธฐ๋ฉด์ ํด๋น ์์น์ ํ์ด์ฌ ๊ฐ์ํ๊ฒฝ์ ์์ฑํ๋ค
Install Framework & Library
poetry add django djangorestframework djangorestframework-simplejwt
- poetry add ๋ช ๋ น์ด๋ฅผ ์ฌ์ฉํด์ django์ ๊ด๋ จ๋ ๊ธฐ๋ฅ๋ค์ ์ค์น ํด ์ฃผ์
Create Django project
- ์์ ๋ช ๋ น์ด๋ค์ ์ด์ฉํ์ฌ ์ค์น๊ฐ ๋ค ๋์๋ค๋ฉด ํ๋ก์ ํธ ์ธํ ์ ํ์
- ๋๋ ํญ์ ํด๋๋ก ๋ค์ด๊ฐ์ ๋ช ๋ น์ด๋ฅผ ์คํํ๊ธฐ ๋๋ฌธ์ config๋ฅผ ๋ง๋ค๊ณ django app๋ค์ ์ถ๊ฐํ๋ ํํ๋ก ๊ฐ๋ฐํ๋ค. ๊ฐ์ ๋ง๋ ๋ฐฉ๋ฒ์ผ๋ก ์งํํ๋ฉด ์ข์ ๊ฒ ๊ฐ๋ค
django-admin startproject config . django-admin startapp workout_auths
Django REST framework ๋ฐ simple-jwt ์ธํ
ํ๋ก์ ํธ๋ฅผ ์ธํ
ํ์ผ๋, ์ฌ์ฉ ํ REST framework ์ simple-jwt๋ฅผ ์ธํ
ํด ์ฃผ์
์ฐ์ Django REST framework๊ณผ simple-jwt ๊ณต์ ๋ฌธ์๋ค์ ์ฒจ๋ถํ๋ค
- Django REST framework : https://www.django-rest-framework.org/
- Django REST framework Simple-Jwt : https://django-rest-framework-simplejwt.readthedocs.io/en/latest/index.html
์ค์ ์ถ๊ฐ
๊ฐ๋ฐ ํ๊ฒฝ๋ง๋ค settings.py๋ฅผ ๋๋๋ฉด ์ข์ง๋ง, ๋์ค์ ๋ค์ ํฌ์คํ
ํ๊ธฐ๋ก ํ๊ณ ์ฐ์
settings.py
์์ ๊ตฌํ ํด ๋ณด์์ฐ์ ์ฌ์ฉ ํ ์ฑ๋ค์ ์ถ๊ฐ ํด ์ฃผ์
settings.py - ์ฑ ์ถ๊ฐ
INSTALLED_APPS = [
"workout_auths",
"rest_framework_simplejwt",
"rest_framework_simplejwt.token_blacklist",
]
settings.py - REST framework setting ์ถ๊ฐ
REST_FRAMEWORK = {
"DEFAULT_PERMISSION_CLASSES": (
"rest_framework.permissions.IsAuthenticated",
),
"DEFAULT_AUTHENTICATION_CLASSES": (
"rest_framework_simplejwt.authentication.JWTAuthentication",
),
"EXCEPTION_HANDLER": "common.exception.handle_exception",
}
SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": datetime.timedelta(days=1),
"REFRESH_TOKEN_LIFETIME": datetime.timedelta(days=10),
"ROTATE_REFRESH_TOKENS": True,
"BLACKLIST_AFTER_ROTATION": True,
"UPDATE_LAST_LOGIN": True,
"ALGORITHM": "HS256",
"SIGNING_KEY": SECRET_KEY,
"VERIFYING_KEY": None,
"AUDIENCE": "woolba.dev.workout.user",
"ISSUER": "woolba.dev.workout",
"JWK_URL": None,
"LEEWAY": 0,
"AUTH_HEADER_TYPES": ("Bearer",),
"AUTH_HEADER_NAME": "HTTP_AUTHORIZATION",
"USER_ID_FIELD": "uuid",
"USER_ID_CLAIM": "user_id",
"USER_AUTHENTICATION_RULE": "rest_framework_simplejwt.authentication.default_user_authentication_rule",
"AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.AccessToken",),
"TOKEN_TYPE_CLAIM": "token_type",
"TOKEN_USER_CLASS": "rest_framework_simplejwt.models.TokenUser",
"JTI_CLAIM": "jti",
"SLIDING_TOKEN_REFRESH_EXP_CLAIM": "refresh_exp",
"SLIDING_TOKEN_LIFETIME": datetime.timedelta(minutes=5),
"SLIDING_TOKEN_REFRESH_LIFETIME": datetime.timedelta(days=1),
}
- EXCEPTION_HANDLER ์ ๊ฐ์ ๊ฒฝ์ฐ๋ ๋์ค์ ๋ค์ ๋ค๋ฃจ๋๋ก ํ๊ฒ ๋ค. Django์์ ๋ฐ์ํ Exception๋ค์ ์ฒ๋ฆฌ ํด ์ฃผ๋ ์ญํ ์ ํ๋ค
settings.py - Custom User Model ๋ง๋ค์ด์ ์ถ๊ฐ
- ์ง์ ๋ง๋ ์ ์ ์ ๋ณด ๋ชจ๋ธ์ ์ฌ์ฉํ๋ ค๊ณ ํ๋ค. ๊ด๋ จ ๋ด์ฉ์ ๋ฐ๋ก ์์ฑ ํด ๋์๊ฒ์ด๋ผ์ ์์ธํ ์ค๋ช ํ์ง ์์ผ๋ ค๊ณ ํ๋ค
- ์ฐ๋ฆฌ๋ Custom User Model์ ์ง์ ๋ง๋ค์ด์ ์ฌ์ฉํ๋ ค๊ณ ํ๋ค. Django์์ ์ด๋ฏธ ๊ตฌํ๋์ด์๋ ์ฌ์ฉ์ ์์ฑ ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ ค๊ณ ํ๋ค.
- ์ฌ์ฉ์ ์์ฑ ๋งค๋์ ํด๋์ค ๋ง๋ค๊ธฐ
from django.contrib.auth.base_user import BaseUserManager
class UserManager(BaseUserManager):
use_in_migrations = True
def create_user(self, nickname, email, password):
if not email:
raise ValueError("์ด๋ฉ์ผ์ ํ์๊ฐ์
๋๋ค.")
user = self.model(nickname=nickname, email=self.normalize_email(email))
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, nickname, email, password):
user = self.create_user(
nickname=nickname, email=self.normalize_email(email), password=password
)
user.is_admin = True
user.is_superuser = True
user.is_staff = True
user.save(using=self._db)
return user
def get_by_natural_key(self, username):
return self.get(**{self.model.USERNAME_FIELD: username, "is_active": True})
- ์์ฑํ UserManger ํด๋์ค๋ฅผ ์ฌ์ฉํ์ฌ Custom User Model ๋ง๋ค๊ธฐ
from django.contrib.auth.base_user import AbstractBaseUser
from django.contrib.auth.models import AbstractUser, PermissionsMixin
from django.db import models
from common.models import uuid_string, BaseModel
from workout_auths.workout_user_manager import UserManager
class WorkoutUserModel(AbstractBaseUser, PermissionsMixin):
class LoginPlatform(models.TextChoices):
KAKAO = ("kakao", "Kakao")
GOOGLE = ("google", "Google")
APPLE = ("apple", "Apple")
WORKOUT = ("workout", "Workout")
class GenderChoices(models.TextChoices):
MALE = ("male", "Male")
FEMALE = ("female", "Female")
NONE = ("none", "None")
class Meta:
db_table = "workout_users"
managed = True
unique_together = (("email", "nickname"),)
objects = UserManager()
USERNAME_FIELD = "nickname"
REQUIRED_FIELDS = ["email"]
uuid = models.CharField(max_length=32, default=uuid_string, unique=True)
nickname = models.CharField(unique=True, max_length=255, blank=True, null=True)
username = models.CharField(unique=False, max_length=255)
phone_number = models.CharField(unique=False, max_length=255, null=True)
email = models.EmailField(
max_length=255,
unique=False,
null=False,
)
password = models.CharField(max_length=255, blank=True, null=True)
login_platform = models.CharField(
max_length=255,
choices=LoginPlatform.choices,
null=False,
)
gender = models.CharField(
max_length=255,
choices=GenderChoices.choices,
default=GenderChoices.NONE,
)
refresh_token = models.TextField(blank=True, null=True)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
date_joined = models.DateTimeField(auto_now_add=True)
- ์ ์ ๋ชจ๋ธ์ ์์ฑํ
WorkoutUserModel
๋ก ์ฌ์ฉํ๊ธฐ
# settings.py
AUTH_USER_MODEL = "workout_auths.WorkoutUserModel"
์ด์ ๋ง์ด๊ทธ๋ ์ด์
์ ํด ์ฃผ๋ฉด ์ฐ๋ฆฌ๊ฐ ๋ง๋ ์ ์ ๋ชจ๋ธ์ด ์์ฑ๋๋ค
- ์ฌ์ฉ ํ ์ ์ ๋ฅผ
createsuperuser
๋ฅผ ํตํด ๋ง๋ค์ด์ฃผ์
python manage.py createsuperuser
url ์ธํ
๊ฐ์ฅ ๊ธฐ๋ณธ ์ธํ
ํ์ผ์ด ๋ค์ด์๋ ๊ณณ์ urls.py๋ก ๋ค์ด๊ฐ๋ฉด url ์ ์ถ๊ฐ ํด ์ค ์ ์๋ค.
config/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path("admin/", admin.site.urls),
path("api/v1/auths/", include("workout_auths.urls")),
]
์์ฒ๋ผ url์ ์ธํ
ํ๋ค๋ฉด workout_auths ์ฑ์ ๋ค์ด๊ฐ์ url์ ๋ง์ ์ด์ด์ฃผ์
workout_auths/urls.py
from django.urls import path
from rest_framework_simplejwt.views import (
TokenVerifyView,
)
from workout_auths.views import (
WorkoutTokenPairView,
WorkoutRefreshTokenPairView,
VerifyMeView,
)
urlpatterns = [
path(
"token",
WorkoutTokenPairView.as_view(),
name="token_obtain_pair",
),
path(
"token/refresh",
WorkoutRefreshTokenPairView.as_view(),
name="token_refresh",
),
path("token/verify", TokenVerifyView.as_view(), name="token_verify"),
path("verify-me", VerifyMeView.as_view(), name="verify_me"),
]
- rest_framework_simplejwt์ ์์ฑ๋์ด์๋
TokenObtainPairView, TokenRefreshView
๋ฅผ ์์๋ฐ์์WorkoutTokenPairView
ํด๋์ค์WorkoutRefreshTokenPairView
๋ฅผ ๋ง๋ค์ด ์ฃผ๋ ค๊ณ ํ๋ค.
- ์ฌ์ฉ์ ๋ก๊ทธ์ธ :
/api/v1/auths/token
- ์ฌ์ฉ์ ๋ก๊ทธ์ธ์ ๋ณด ์ฌ์์ฑ :
/api/v1/auths/token/refresh
- ์ฌ์ฉ์ ๋ก๊ทธ์ธ์ ๋ณด๊ฐ ์์ด์ผ ์ ๊ทผ์ด ๊ฐ๋ฅํ url (ํ
์คํธ์ฉ) :
/api/v1/auths/verify-me
Token ๋ฐ๊ธ ๊ธฐ๋ฅ ์์ฑ
- Class Based View ๋ก ์์ฑ ํ๊ณ ํฌ๊ฒ ๊ตฌํํ์ง ์์๋ ๋๋ ๋ถ๋ถ๋ค์
djangorestframework-simplejwt
์์ ์์๋ฐ์ ์ฌ์ฉํ๋ค
- simple-jwt๋ง ์ฌ์ฉํด์ ๋ก๊ทธ์ธํ๋ฉด
access
์refresh
๊ฐ๋ง ์ฃผ์ด์ ๋๋ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ์์ธํ ์๊ณ ์ถ๋ค๋ ์๊ฐ์ ์ฌ๋ฌ ์ ๋ณด๋ค์ ๋ฃ์ด๋ณด์๋ค
WorkoutTokenCustomSerializer
class WorkoutTokenCustomSerializer(TokenObtainPairSerializer):
default_error_messages = {
"no_active_account": {
"result": None,
"message": "์ฌ์ฉ์ ์ ๋ณด๊ฐ ์ฌ๋ฐ๋ฅด์ง ์์ต๋๋ค.",
"data": [],
},
}
@classmethod
def get_token(cls, user):
token = super().get_token(user)
# Add custom claims
token["nickname"] = user.nickname
token["phone_number"] = user.phone_number
token["gender"] = user.gender
return token
def validate(self, attrs):
data = super(WorkoutTokenCustomSerializer, self).validate(attrs)
refresh_token = str(self.get_token(self.user))
self.user.refresh_token = refresh_token
data["refresh"] = refresh_token
data["nickname"] = self.user.nickname
data["phone_number"] = self.user.phone_number
data["gender"] = self.user.gender
result = dict()
result["data"] = data
result["message"] = "๋ก๊ทธ์ธ์ ์ฑ๊ณตํ์ต๋๋ค."
result["success"] = True
self.user.save()
return result
View
- ์์ Serializer๋ง ์์ฑ์ด ๋์๋ค๋ฉด View๋
TokenObtainPairView
๋ฅผ ์์๋ฐ์ ๊ธ๋ฐฉ ์์ฑํ๋ค
WorkoutTokenPairView
๋ ๋ก๊ทธ์ธ ๊ณผ์ ์ด๊ธฐ๋๋ฌธ์ ๋ชจ๋ ์ฌ์ฉ์๊ฐ ์ ๊ทผ์ด ๊ฐ๋ฅํ๋ค
class WorkoutTokenPairView(TokenObtainPairView):
permission_classes = (permissions.AllowAny,)
serializer_class = WorkoutTokenCustomSerializer
๋ก๊ทธ์ธ Url ํ ์คํธ
curl -X POST \ http://localhost:8000/api/v1/auths/token \ -H 'content-type: application/json' \ -d '{ "username": "paul", "password": "login_password_123" }'
{
"data": {
"refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTcwNTA3NTYzNCwiaWF0IjoxNzA0MjExNjM0LCJqdGkiOiJlODU5ZTA5N2U5MjE0N2E3ODRkNDM0NjVkNTY1M2EyNCIsInVzZXJfaWQiOiJmNmViODgyZGQ3MmM0ODljYWNlMjI0ZDc4NjM5M2FhMSIsIm5pY2tuYW1lIjoicGF1bCIsInBob25lX251bWJlciI6bnVsbCwiZ2VuZGVyIjoibm9uZSIsImF1ZCI6Indvb2xiYS5kZXYud29ya291dC51c2VyIiwiaXNzIjoid29vbGJhLmRldi53b3Jrb3V0In0.yvK3hfa5II4MvJNHYSJ0yVusbMllJeadvOpHib6VK6c",
"access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzA0Mjk4MDM0LCJpYXQiOjE3MDQyMTE2MzQsImp0aSI6ImQ5ZDg4YTc3MGFkMTQyZDNiMjM3MDc3YjllZmEyM2NmIiwidXNlcl9pZCI6ImY2ZWI4ODJkZDcyYzQ4OWNhY2UyMjRkNzg2MzkzYWExIiwibmlja25hbWUiOiJwYXVsIiwicGhvbmVfbnVtYmVyIjpudWxsLCJnZW5kZXIiOiJub25lIiwiYXVkIjoid29vbGJhLmRldi53b3Jrb3V0LnVzZXIiLCJpc3MiOiJ3b29sYmEuZGV2LndvcmtvdXQifQ.zIezjOc1qx_8gsAB14lw9YL90Kh_KeGM1_VBzp_rxmk",
"nickname": "paul",
"phone_number": null,
"gender": "none"
},
"message": "๋ก๊ทธ์ธ์ ์ฑ๊ณตํ์ต๋๋ค.",
"success": true
}
Token Refresh ๊ธฐ๋ฅ ์์ฑ
- Token Refresh๊ธฐ๋ฅ์ simple-jwt์์
access
ํ ํฐ๋ง ๋ฆฌํด๋์๋ค
- Access ํ ํฐ์ ์ฌ๋ฐ๊ธ ํด ์ฃผ๋ฉด์ Refresh ๋ ์ฌ์์ฑํด์, Refresh ํ ํฐ์๋ํ ๋ณด์๋ ์ง์ผ์ฃผ๋ ค๊ณ ํ๋ค
- ์ฌ์ฉ ํ Refresh์ ์ฌ์ฌ์ฉํ์ง ๋ชปํ๊ฒ ํ๋ ๊ธฐ๋ฅ์
djangorestframework-simplejwt
์์ ์ด๋ฏธ ์ ์ํ๊ณ ์๋ค. - settings.py ์ SIMPLE_JWT ํญ๋ชฉ์์ ์๋์ ๋ ํญ๋ชฉ์ True๋ก ์ค์ ํด ์ฃผ๊ณ ,
"ROTATE_REFRESH_TOKENS": True,
"BLACKLIST_AFTER_ROTATION": True,
"rest_framework_simplejwt.token_blacklist"
WorkoutRefreshTokenCustomSerializer
- ์๋์ TokenRefreshSerializer ์๋ Refreshํ ํฐ ์ฌ๋ฐ๊ธ ๋ฐ ๋ฐ์ดํฐ ์ถ๊ฐ์ ๋ํ ๋ด์ฉ์ด ์์ผ๋ฏ๋ก ์ถ๊ฐ ํด ์ค๋ค
class WorkoutRefreshTokenCustomSerializer(TokenRefreshSerializer):
def validate(self, attrs):
data = super(WorkoutRefreshTokenCustomSerializer, self).validate(attrs)
decode_access_token = token_backend.decode(data["access"], verify=True)
user_model = get_user_model()
user = user_model.objects.get(uuid=decode_access_token.get("user_id"))
refresh_token = RefreshToken.for_user(user)
data["refresh"] = str(refresh_token)
user.refresh_token = refresh_token
user.save()
data["nickname"] = user.nickname
data["phone_number"] = user.phone_number
data["gender"] = user.gender
result = dict()
result["data"] = data
result["message"] = "๋ก๊ทธ์ธ์ ์ฑ๊ณตํ์ต๋๋ค."
result["success"] = True
return result
View
- ์์ Serializer๋ง ์์ฑ์ด ๋์๋ค๋ฉด View๋
TokenRefreshView
๋ฅผ ์์๋ฐ์ ๊ธ๋ฐฉ ์์ฑํ๋ค
WorkoutRefreshTokenPairView
๋ ๋ก๊ทธ์ธ ๊ณผ์ ์ด๊ธฐ๋๋ฌธ์ ๋ชจ๋ ์ฌ์ฉ์๊ฐ ์ ๊ทผ์ด ๊ฐ๋ฅํ๋ค
class WorkoutRefreshTokenPairView(TokenRefreshView):
permission_classes = (permissions.AllowAny,)
serializer_class = WorkoutRefreshTokenCustomSerializer
ํ ํฐ ์ฌ๋ฐ๊ธ Url ํ ์คํธ
curl -X POST \ http://localhost:8000/api/v1/auths/token/refresh \ -H 'content-type: application/json' \ -d '{ "refresh":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTcwNTA3NTY0MywiaWF0IjoxNzA0MjExNjQzLCJqdGkiOiJmNjE5NDcyYTE0ZTI0OWUzOTU3ZDQ2NmFlM2JiYTM1MyIsInVzZXJfaWQiOiJmNmViODgyZGQ3MmM0ODljYWNlMjI0ZDc4NjM5M2FhMSIsImF1ZCI6Indvb2xiYS5kZXYud29ya291dC51c2VyIiwiaXNzIjoid29vbGJhLmRldi53b3Jrb3V0In0.uw6Be-8TsmEFq5p4Om4cUCRFrijtAfhqC4ain3runqA" }'
{
"data": {
"access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzA0Mjk4MDUyLCJpYXQiOjE3MDQyMTE2NDMsImp0aSI6ImVjMTlkYjI3YTM5YTRhZGM5NTY0MTIzNTA2NGE0YWJkIiwidXNlcl9pZCI6ImY2ZWI4ODJkZDcyYzQ4OWNhY2UyMjRkNzg2MzkzYWExIiwiYXVkIjoid29vbGJhLmRldi53b3Jrb3V0LnVzZXIiLCJpc3MiOiJ3b29sYmEuZGV2LndvcmtvdXQifQ.ddxXUk-O-b4PdjsXAyYC_mCwEZIV80JlMpwiO6hdPLU",
"refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTcwNTA3NTY1MiwiaWF0IjoxNzA0MjExNjUyLCJqdGkiOiI0NjZmNjNmNWQzNWI0OTdhYjM0NDQzYTU5ZjM2N2ZmNyIsInVzZXJfaWQiOiJmNmViODgyZGQ3MmM0ODljYWNlMjI0ZDc4NjM5M2FhMSIsImF1ZCI6Indvb2xiYS5kZXYud29ya291dC51c2VyIiwiaXNzIjoid29vbGJhLmRldi53b3Jrb3V0In0.gedUd--5JryMwiP4golRR1WKXKDs1v2zrzPBwaXJYqQ",
"nickname": "paul",
"phone_number": null,
"gender": "none"
},
"message": "๋ก๊ทธ์ธ์ ์ฑ๊ณตํ์ต๋๋ค.",
"success": true
}
์ ์ฒด ๊ณผ์ ํ ์คํธ
- ์ง๊ธ๊น์ง ์์ฑ ํ url ๊ณผ authenication token ์ ํ ์คํธ ํด ๋ณด๋ ค๊ณ ํ๋ค
- ๋ก๊ทธ์ธ ํด์ผ๋ง ์ฌ์ฉ ํ ์ ์๋ url์
path("verify-me", VerifyMeView.as_view(), name="verify_me"),
๋ก ๊ฐ๋จํ๊ฒ ์ ์ํ๊ณ return 200 ๋ง ์ ์ ํด ์ฃผ์๋ค
Test.py
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from workout_auths.models import WorkoutUserModel
from rest_framework_simplejwt.tokens import RefreshToken
class JWTAuthTests(APITestCase):
def setUp(self):
# Create a user for testing
self.user = WorkoutUserModel.objects.create_user(
nickname="test_user",
email="test_user@mail.com",
password="qwerqwer123",
)
# URL for obtaining tokens
self.token_url = reverse("token_obtain_pair")
# URL for a protected view (change this to your view)
self.protected_url = reverse("verify_me")
def test_token_obtain(self):
# Test token obtain
response = self.client.post(
self.token_url, {"nickname": "test_user", "password": "qwerqwer123"}
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertTrue("access" in response.data.get("data"))
self.assertTrue("refresh" in response.data.get("data"))
def test_token_refresh(self):
# Test token refresh
refresh = RefreshToken.for_user(self.user)
response = self.client.post(reverse("token_refresh"), {"refresh": str(refresh)})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertTrue("access" in response.data.get("data"))
self.assertTrue("refresh" in response.data.get("data"))
def test_protected_view_access(self):
# Test access to a protected view
self.client.credentials(
HTTP_AUTHORIZATION="Bearer "
+ str(RefreshToken.for_user(self.user).access_token)
)
response = self.client.get(self.protected_url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
# Test access with invalid token
self.client.credentials(HTTP_AUTHORIZATION="Bearer " + "invalidtoken")
response = self.client.get(self.protected_url)
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
Share article