본문으로 건너뛰기
Advertisement

Django 인증 시스템

Django는 세션 기반 인증을 기본 제공하며, SimpleJWT로 JWT 토큰 인증을 추가할 수 있습니다.


커스텀 User 모델

# users/models.py
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from django.db import models


class UserManager(BaseUserManager):
def create_user(self, email: str, password: str = None, **extra_fields):
if not email:
raise ValueError("이메일은 필수입니다")
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user

def create_superuser(self, email: str, password: str, **extra_fields):
extra_fields.setdefault("is_staff", True)
extra_fields.setdefault("is_superuser", True)
return self.create_user(email, password, **extra_fields)


class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True)
name = models.CharField(max_length=150)
phone = models.CharField(max_length=20, blank=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_email_verified = models.BooleanField(default=False)
date_joined = models.DateTimeField(auto_now_add=True)

objects = UserManager()

USERNAME_FIELD = "email" # 로그인 필드
REQUIRED_FIELDS = ["name"] # createsuperuser 필수 입력

class Meta:
db_table = "users"

def __str__(self):
return self.email

def get_full_name(self):
return self.name
# settings.py
AUTH_USER_MODEL = "users.User" # 반드시 첫 migrate 전에 설정!

JWT 인증 — SimpleJWT

pip install djangorestframework-simplejwt
# settings.py
from datetime import timedelta

INSTALLED_APPS = [..., "rest_framework_simplejwt"]

REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework_simplejwt.authentication.JWTAuthentication",
],
}

SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=30),
"REFRESH_TOKEN_LIFETIME": timedelta(days=7),
"ROTATE_REFRESH_TOKENS": True, # 리프레시 시 새 토큰 발급
"BLACKLIST_AFTER_ROTATION": True, # 이전 토큰 블랙리스트 등록
"ALGORITHM": "HS256",
"AUTH_HEADER_TYPES": ("Bearer",),
"USER_ID_FIELD": "id",
"USER_ID_CLAIM": "user_id",
}
# users/urls.py
from django.urls import path
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
TokenVerifyView,
TokenBlacklistView,
)
from . import views

urlpatterns = [
# JWT 기본 엔드포인트
path("login/", TokenObtainPairView.as_view(), name="token_obtain_pair"),
path("refresh/", TokenRefreshView.as_view(), name="token_refresh"),
path("verify/", TokenVerifyView.as_view(), name="token_verify"),
path("logout/", TokenBlacklistView.as_view(), name="token_blacklist"),

# 커스텀 엔드포인트
path("register/", views.RegisterView.as_view(), name="register"),
path("me/", views.CurrentUserView.as_view(), name="me"),
path("change-password/", views.ChangePasswordView.as_view(), name="change-password"),
]

커스텀 JWT 페이로드

# users/serializers.py
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework_simplejwt.views import TokenObtainPairView


class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
@classmethod
def get_token(cls, user):
token = super().get_token(user)

# 토큰에 추가 정보 포함
token["email"] = user.email
token["name"] = user.name
token["role"] = "admin" if user.is_staff else "user"

return token


class MyTokenObtainPairView(TokenObtainPairView):
serializer_class = MyTokenObtainPairSerializer

회원가입 / 현재 사용자 API

# users/views.py
from rest_framework import generics, status, permissions
from rest_framework.response import Response
from rest_framework.views import APIView
from django.contrib.auth import get_user_model
from .serializers import UserRegisterSerializer, UserSerializer, ChangePasswordSerializer

User = get_user_model()


class RegisterView(generics.CreateAPIView):
"""POST /auth/register/ — 회원가입"""
queryset = User.objects.all()
serializer_class = UserRegisterSerializer
permission_classes = [permissions.AllowAny]


class CurrentUserView(generics.RetrieveUpdateAPIView):
"""GET/PUT /auth/me/ — 현재 사용자 정보"""
serializer_class = UserSerializer
permission_classes = [permissions.IsAuthenticated]

def get_object(self):
return self.request.user


class ChangePasswordView(APIView):
"""POST /auth/change-password/ — 비밀번호 변경"""
permission_classes = [permissions.IsAuthenticated]

def post(self, request):
serializer = ChangePasswordSerializer(data=request.data)
serializer.is_valid(raise_exception=True)

user = request.user
if not user.check_password(serializer.validated_data["old_password"]):
return Response(
{"error": "현재 비밀번호가 틀렸습니다"},
status=status.HTTP_400_BAD_REQUEST,
)

user.set_password(serializer.validated_data["new_password"])
user.save()
return Response({"message": "비밀번호가 변경되었습니다"})

소셜 로그인 (django-allauth)

pip install django-allauth
# settings.py
INSTALLED_APPS = [
...
"django.contrib.sites",
"allauth",
"allauth.account",
"allauth.socialaccount",
"allauth.socialaccount.providers.google",
"allauth.socialaccount.providers.github",
]

SITE_ID = 1
AUTHENTICATION_BACKENDS = [
"django.contrib.auth.backends.ModelBackend",
"allauth.account.auth_backends.AuthenticationBackend",
]

ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_AUTHENTICATION_METHOD = "email"
ACCOUNT_USERNAME_REQUIRED = False
SOCIALACCOUNT_PROVIDERS = {
"google": {
"SCOPE": ["profile", "email"],
"AUTH_PARAMS": {"access_type": "online"},
}
}

정리

구성 요소역할
AbstractBaseUser커스텀 User 모델 베이스
AUTH_USER_MODEL커스텀 모델 지정 (초기화 전 설정)
SimpleJWTJWT 인증 (Access + Refresh Token)
ROTATE_REFRESH_TOKENS리프레시 시 토큰 순환
TokenObtainPairSerializerJWT 페이로드 커스터마이징
django-allauth소셜 로그인 (Google, GitHub 등)

AUTH_USER_MODEL첫 번째 migrate 전에 반드시 설정해야 합니다. 이후 변경은 매우 복잡합니다.

Advertisement