(Django) user 커스터마이징하기(UserManager, AbstractBaseUser)
장고에는 기본적으로 user모델이 정의되어 있다. 기본적으로 정의 되어 있는 모델보다 더 다양한 정보를 입력받을 수 있게 하기 위해 Profile 모델을 생성해 User모델과 1:1로 묶어 구현하는 방식으로 프로젝트를 진행하다가 장고 v1.5 이후 user model을 커스텀하는 것이 일반적인 방법이 되었고 한번쯤은 user모델을 커스텀하는 방식을 정리하여 사용해보고 싶었다. 이다. 나는 AbstractBaseUser를 사용하여 user를 커스텀 할 예정이다
AbstractBaseUser
로그인의 아이디를 이메일 주소로 사용하도록 하거나 장고의 로그인 절차가 아닌 인증 절차를 직접 구현하고 싶을때 사용한다. 즉 로그인 방식을 변경하고 싶을때나 원하는 필드들로 유저 모델을 구성하고 싶을때 사용하면 된다. 이때 해당 모델로 구현하기 위해서는 BaseUserManager를 상속하는 UserManager를 함께 정의하여 일반유저와 슈퍼유저의 생성 방식을 정의해주어야 한다. 나는 사실 UserManager의 역할을 이해하는 것부터 힘들었었다. 커스텀하는 방식이 이해가 잘 안가서 4일정도 헤맸던것 같다..(알고나니 생각보다 쉬웠다😂)
내가 구현할 User모델의 요구사항
사용자의 아이디를 이메일로 받아 후에 이메일 인증을 구현한다
사용자의 닉네임과 이미지의 부가 정보를 받는다
이메일 인증을 위해 초반에 회원가입 하는 유저의 is_active의 default값을 False로 받는다
슈퍼유저의 is_active와 is_superuser의 기본값은 True로 받는다
class UserManager(BaseUserManger)의 역할
AbstractBaseUser를 이용해 커스텀할때 가장 먼저 UserManager를 정의해주고 나중에 User 클래스를 작성할때 object = UserMnager()를 넣어주는 모습을 누구나 봤을 것이다. 이 부분을 이해하기 힘들었다.
일단 UserManager는 헬퍼 클래스라고 불리며 두가지의 함수를 가지고 있다. 이 클래스는 간단하게 말해서 생성되는 사용자들을 등록해주는 역할을 한다. 일반 유저를 등록할때 일반 유저가 가질수 있는 권한이나 값들을 미리 메서드로 정의해두고 해당 메서드를 통해서 일반 유저가 모델에 담기게 된다. 이때 create_user 가 바로 이 메서드의 역할을 해준다고 생각하면 된다. 그렇다면 자연스럽게 create_superuser는 슈퍼유저를 생성할때 사용되는 메서드라는 것을 예상할 수 있을 것이다. 사실 이 클래스 내부에 메서드가 쓰이는 방법이 여러가지라 더 헷갈렸던 것 같다. 그 중 가장 기본적으로 사용되는 형태를 사용하였다.
create_user
create_superuser
코드로 이해해보자
class UserManager(BaseUserManager):
def _create_user(self, email, password=None, **extra_fields):
if not email:
raise ValueError('Users must have an email address')
email = self.normalize_email(email) # 이메일 주소를 소문자로 변환하는 과정을 거친뒤 저장
user = self.model(email=email, **extra_fields) # 사용자 모델 객체를 생성
user.set_password(password) # 패스워드를 장고에서 제공해주는 해시화 과정을 거쳐 저장 (set_password메서드 안에 make_password가 해시화를 진행해줌)
user.save(using=self._db) # db에 사용자 정보를 저장
return user
def create_user(self, email, password, **extra_fields):
extra_fields.setdefault('is_superuser', False) # BaseUsrManager에 정의되어 있는 is_superuser 필드의 기본값을 False로 정의
return self._create_user(email, password, **extra_fields)
def create_superuser(self, email, password, **extra_fields):
extra_fields.setdefault('is_active', True) # 슈퍼유저 생성이기 때문에 is_active등 슈퍼유저가 가져야 할 권한들의 기본값을 True로 정의
extra_fields.setdefault('is_superuser', True)
extra_fields.setdefault('is_staff', True)
return self._create_user(email, password, **extra_fields)
위와 같은 코드를 많이 봤을거다. 커스텀할때 필요한 클래스인데 위에서 말한것처럼 유저가 생성될때 항상 UserManager 메서드를 거쳐 모델에 저장된다. 위에서는 2가지의 함수가 있다고 했지만 코드를 보면 총 3가지의 함수가 존재한다. 나는 create_user, create_superuser 메서드를 생성하여 이에 맞게 유저의 기본값들을 각각의 메서드에서 변경해준 뒤 _create_user 메서드로 변경한 값들을 매개변수로 넣어 리턴해주었다. 그후 _create_user가 실행되어 받은 값들을 토대로 실제 user 모델의 객체로 값들을 넣어준다. 위키독스에서는 2가지의 메서드를 사용하는데 차이를 보고 싶다면 링크를 눌러 확인해보자. https://wikidocs.net/10294
그럼 이제 대충 UserManager의 역할이 무엇인지 감이 왔을거다. 이제 완벽하게 이해해보자. 만약 createsuperuser로 슈퍼 유저를 생성한다고 할때 UserManager에서 어떤 메서드가 실행 될 것인지 생각해보자. create_superuser 메서드가 실행된다. :-) 그렇다면 어떤 데이터가 메서드의 파라미터로 들어갈지 생각해보자
password
**extra_fields
이렇게 3가지의 값들이 파라미터로 들어가게 되는데 email과 password는 필수값이기 때문에 명시적으로 작성해주었다. 그렇다면 extra_fields에는
뭐가 들어있을까 ? 일단 각자의 알규먼트에 무엇이 들어있는지 확인하고 싶다면 for문을 돌려 확인해보는 방법이 있다 (나는 이방법으로 이해했다)
다른 방법은 단순하게 User 클래스 내부에 있는 이 부분을 확인하면 된다.
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['nickname']
username_field는 아이디를 username으로 받을지 email로 받을지 다른 값으로 받을지 정할때 이곳에 작성해주면 된다. 나는 email을 아이디로 쓸 예정이였기 때문에 이곳에 email을 작성해주었고 실제로 createsuperuser를 하게 되면 가장 먼저 email이 뜨게 될 것이다. 그리고 required_fields가 바로 아이디 ,pwd 외에 값을 받고 싶을때 작성해주는 곳이다. 이 required_fields는 다른곳에는 적용되지 않고 superuser를 만들때 나오는 값들이라고 생각하면 된다. 위처럼 쓰고 난 후 createsuperuser를 해주면 email, pwd , nickname를 입력하도록 되어 있을 것이다.
여기서 또 알아야 할 한가지는 extra_fields.setdefault('is_superuser', False)
이 setdefault 이다. 메서드 안에 작성해주면
해당 값 또한 extra_fields에 담기게 된다는 것을 알고 있자 -!
여기서 상속에 대한 이해가 없다면 분명 이해하기 힘든 부분들이 생길것이다. 내가 그랬다. 아래의 코드는 User 클래스 부분이다. 저기에 써진 필드들이 User가 생성될때 모델에 들어가는 필드들이다. 이때 이곳에 명시적으로 작성을 해주었다면 UserManager에서 해당 필드들의 기본값등을 (is_staff를 생각하면 된다) 변경해서 User 모델에 넣을 수 있다.(Usermanager를 통해 user가 생성되기 때문에) 이때 아래에는 is_superuser 필드가 존재하지 않지만 UserManager에서는 is_superuser를 setdefault를 이용해 디폴트 값을 변경해준다. 이게 가능한 이유는 is_superuser는 AbstractBaseUser내부에 정의가 되어 있기 때문이다. User가 AbstractBaseUser를 상속받기 때문에 필드에 작성하지 않아도 사용할 수 있는 것이다.(AbstractBaseUser를 눌러서 눈으로 확인해보면 이해가 될것이다)
class User(AbstractBaseUser, PermissionsMixin, TimeStampable, Deleteable):
image = models.TextField(verbose_name=('Profile image'),)
email = models.EmailField(verbose_name=('Email address'),max_length=255,unique=True,)
nickname = models.CharField(verbose_name=('Nickname'),max_length=30,unique=True)
is_active = models.BooleanField(verbose_name=('Is active'),default=False)
is_staff = models.BooleanField(verbose_name=('Is staff'),default=False)
objects = UserManager()
이정도면 대충이라도 감은 잡았을거라고 생각한다. (난 여기까지 이해하는데 굉장히 많은 시간이 걸렸다.) 사실 코드가 전부 비슷해서 가져다 써도 큰 문제가 되지는 않지만 이해하고 쓰는 코드와 이해하지 못한 코드를 쓰는 것에는 정말 엄청난 차이가 있다.
User 클래스를 통해 user가 생성이 되는데 이때 UserManger의 메서드가 유저를 모델에 넣어준다. User 클래스에 작성된 필드들과 함께 미리 상속 받은 값들의 기본값들을 변경시켜 함께 유저 필드에 넣어 줄 수 있다.
이 정도가 기본인 것 같다. 이후에 PermissionsMixin등을 통해 여러 권한을 주는 방식이라던지 잘 정리되어 있는 블로그를 참고 하면 분명 이해하기가 수월할 것이라고 생각한다. :-)