장고에서 기본으로 제공하는 User 클래스를 받아와 별도의 개발 없이 사용자를 관리할 수 있습니다.
하지만, 아래의 두 가지 이유로 사용자 클래스를 직접 설계해야 하는 상황이 발생합니다.
- Django 기본 User 클래스의 사용자 인증 방식 - username으로 인증
- Django 기본 User 클래스가 제공하는 필드가 제한적임 - username, first_name, last_name, email, password, groups, user_permissions, is_staff, is_active,is_superuser, last_login, date_joined
장고의 기본 User모델은 username필드로 사용자를 인증합니다.
닉네임으로 사용자를 인증하기보다, 이메일 혹은 비밀번호 등을 사용해 유저를 인증하고 싶다면 커스텀 모델을 직접 생성해주어야 합니다.
또, 서비스에 프리미엄 멤버십 기능을 도입해 사용자별로 프리미엄 서비스 구독자인지 아닌지를 기록하기 위해 is_premium_member
속성을 추가해야 한다면 역시 기본으로 제공하는 User클래스만으로는 부족합니다.
이렇게, 인증 방식의 변경 혹은 모델에 새로운 속성값을 추가하고자 할 때 커스텀 유저 클래스를 만들어 사용할 수 있습니다.
모든 장고 커스텀 유저 클래스는, AbstractBaseUser
, AbstractUser
, User
클래스 중 하나를 상속받아야 합니다.
오른쪽으로 갈 수록 기존의 기능을 최대한 활용할 수 있지만, 모델 변경의 자율성은 내려갑니다. 프로젝트 상황에 맞는 기능을 적절히 활용하는 것이 좋겠습니다.
저는 가장 많은 코드를 작성해야 하지만, 변경이 가장 자유로운 AbstractBaseUser
클래스를 상속받아 커스텀 유저 모델을 정의해보겠습니다.
아래와 같이 커스텀 유저와 관련된 모든 내용은 user라는 이름의 별도 app에서 관리하려 합니다.
python manage.py startapp user
이후 당연히 root urls.py에 해당 app을 연결해주시고, root settings.py의 installed_app에도 user를 추가해주세요.
이제, user app내의 models.py파일에 유저 모델을 만들어보겠습니다.
아래와 같이 AbstractBaseUser클래스를 상속받음으로서, TravelCommunityUser클래스는 커스텀 유저 클래스로서 기능할 수 있는 토대를 갖추게 됩니다.
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
class TravelCommunityUser(AbstractBaseUser, PermissionsMixin):
PermissionsMixin클래스의 경우, 커스텀 유저 클래스의 권한 설정을 도와주는 클래스입니다. 추후 권한 설정에 사용할 것이니 미리 상속받겠습니다.
from django.db import models
from django.utils.translation import gettext_lazy as _
from datetime import timezone
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
class TravelCommunityUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_('enter your email here'), unique=True)
password = models.CharField(max_length=20, unique=True)
user_name = models.CharField(max_length=50, unique=True)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
joined_date = models.DateField(default=timezone.now)
about_this_user = models.TextField(max_length=1000, blank=True)
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=False)
is_premium_member = models.BooleanField(default=False)
def __str__(self):
return self.user_name
다음과 같이, 모델 내에 원하는 프로퍼티를 모두 작성해줍니다.
gettext_lazy의 경우, 텍스트를 다양한 언어로 번역해주는 기능이니, 필수적으로 포함하실 필요는 없습니다.
커스텀 유저 모델을 생성할 때의 꽃은 아래의 부분이 아닌가 싶습니다.
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['user_name']
이 두 프로퍼티는 장고 프레임워크에서 제공하는 필수 프로퍼티로,
프로퍼티 이름을 임의로 변경할 수 없습니다.
이는 둘째의 문제고, 두 프로퍼티의 기능을 살펴보겠습니다.
앞서 포스팅을 시작하며 장고의 기본 User모델은 username프로퍼티로 사용자를 인증한다 하였습니다.
USERNAME_FIELD
에 원하는 프로퍼티를 전달하면, 장고는 해당 프로퍼티로 요청을 보내온 사용자를 인증하게 됩니다.
저의 경우 email로 설정하였으니, username으로 사용자를 인증하는 기존 방식에서 탈피해 email로 사용자를 인증하게 됩니다.
REQUIRED_FIELDS
에는 슈퍼유저를 생성할 때 필수로 입력해야 하는 프로퍼티를 적어줄 수 있습니다. USERNAME_FIELD에 있는 값은 이곳에 적어주지 않아도 기본으로 필수 입력값이 되니, 저의 경우 email과 user_name을 필수로 받고 있음을 확인할 수 있습니다.
유저 모델을 정의하였다면, 생성한 커스텀 모델에 맞는 매니저 클래스도 직접 생성해주어야 합니다.
매니저 클래스는, 모델 형태에 맞는 인스턴스들의 DB CRUD 연산을 위해 사용하는 클래스입니다.
커스텀 유저 매니저 클래스는 BaseUserManager
클래스를 상속받아야 하며, create_user
와 create_superuser
메소드를 구현해야 합니다.
아래는 완성된 코드입니다.
from django.db import models
from django.utils.translation import gettext_lazy as _
from datetime import timezone
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager
class TravelCommunityUserManager(BaseUserManager):
def create_user(self, email, user_name, first_name, password, **other_fields):
if not email:
raise ValueError(_("이메일 주소는 반드시 입력하셔야 합니다."))
email = self.normalize_email(email)
user = self.model(email=email, user_name=user_name, first_name=first_name, **other_fields)
user.set_password(password)
user.save()
return user
def create_superuser(self, email, user_name, first_name, password, **other_fields):
other_fields.setdefault('is_admin', True)
other_fields.setdefault('is_superuser', True)
other_fields.setdefault('is_active', True)
if other_fields.get('is_admin') is not True or other_fields.get('is_superuser') is not True:
raise ValueError("관리자 권한이 없습니다.")
return self.create_user(email=email, user_name=user_name, first_name=first_name, password=password, **other_fields)
매니저 클래스를 생성하였다면, 추후 디폴트 매니저 클래스가 아닌, 저희가 생성한 TravelCommunityUserManager
클래스를 이용해 유저 클래스의 유저 객체에 접근할 수 있도록, 유저 모델에 아래의 코드를 추가하겠습니다.
objects = TravelCommunityUserManager()
이후 완성된 커스텀 유저 클래스의 코드는 아래와 같습니다.
class TravelCommunityUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_('enter your email here'), unique=True)
password = models.CharField(max_length=20, unique=True)
user_name = models.CharField(max_length=50, unique=True)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
joined_date = models.DateField(default=timezone.now)
about_this_user = models.TextField(max_length=1000, blank=True)
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=False)
is_premium_member = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['user_name']
objects = TravelCommunityUserManager()
def __str__(self):
return self.user_name
이제 추후 TravelCommunityUser.objects.all()
등으로 TravelCommunityUser
커스텀 유저 클래스의 객체에 접근할 때, 장고 디폴트 매니저 클래스가 아닌, 저희가 생성한 커스텀 매니저 클래스를 사용하게 됩니다.
아래는 저희가 생성한 커스텀 매니저 클래스를 이용해 커스텀 유저 모델 스키마에 맞는 새로운 유저 데이터를 생성하는 예제입니다.
from .models import TravelCommunityUser
# 일반 사용자 생성
user = TravelCommunityUser.objects.create_user(email='jinwoo@email.com', user_name='jinwoo', first_name='lee', password='안알려줄거임')
# 슈퍼유저 생성
superuser = TravelCommunityUser.objects.create_superuser(email='admin@example.com', user_name='대장님', first_name='대장이라고', password='안알려줄거임')
# 사용자 조회 예시
all_users = TravelCommunityUser.objects.all()
TravelCommunityUser
클래스 내에서 objects = TravelCommunityUserManager()
로 정의해두었기에, objects
는 저희가 생성한 매니저 클래스가 됩니다.
TravelCommunityUserManager
클래스는 BaseUserManager
클래스를 상속받았기에, 기본적인 매니저 클래스가 제공하는 all()
등의 기능 외에도, create_user
, create_superuser
메소드를 추가로 사용할 수 있습니다.
이제 장고에게 기본 유저 모델의 사용을 중단하고,
저희가 생성한 커스텀 유저 모델을 사용하라고 지시해야 합니다.
해당 지시는 프로젝트 루트 디렉토리의 settings.py 파일에 아래와 같은 내용을 추가해줌으로서 완성될 수 있습니다.
AUTH_USER_MODEL = 'user.TravelCommunityUser'
app이름 명.유저 클래스 이름
형태로 작성해주면 됩니다.
일반적인 migration 작업을 진행해주시면 됩니다.
migration 절차를 잊어버리신 분들을 위해 링크를 남깁니다.
아래의 링크는 해당 내용을 다룬 제 지난 포스팅입니다.
장고는 기본적으로 유저 스키마를 제공하기에, 개발자의 코드 단 한 줄도 없이 사용자 스키마를 제공합니다. 하지만, 기본 제공되는 사용자 모델의 프로퍼티엔 기본적인 사항만이 있어 프로젝트 상황에 따라 특정 프로퍼티를 추가하고 싶은 경우 직접 유저 모델을 생성할 수 있었습니다.
프로퍼티의 추가 외에도, 사용자 인증방식을 username이 아닌 다른 프로퍼티로 지정하기 위해서도 커스텀 유저 모델을 생성할 수 있었습니다.
커스텀 유저 모델, 커스텀 매니저 클래스 생성, 설정 파일 등록.
이렇게 세 단계를 직접 수행하며 더욱 복잡한 유저 스키마를 구성할 수 있겠습니다.
이상으로 글을 마칩니다.
감사합니다.
참고자료
https://www.youtube.com/watch?v=Ae7nc1EGv-A
https://docs.djangoproject.com/en/5.0/topics/auth/customizing/ [공식문서]