[Django Authentication System의 모든 것] #1 - 기본 User 모델

dev-dolxegod·2021년 7월 3일
11

소개

*해당 시리즈는 독자분들이 Django에 대한 기본적인 경험 (장고 어드민, 장고 기본 로그인 구축 등)을 가지고 계시다는 가정 하에 작성된 글입니다. Django 자체를 전혀 사용해보시지 않으신 분들에겐 이해가 어려울 수 있습니다.

*해당 시리즈는 Django 3.2 버전을 기준으로 작성되었습니다.

장고를 경험하신 분들이라면, 처음 장고 프로젝트를 만든 후 첫 migrate를 했을 때 아래 사진처럼 Model에서 생성하지도 않은 (!) 테이블들이 DB에 잔뜩 생성된 경험이 있으실 겁니다.

당황하지 마세요 :) 전부 여러분들의 빠른 웹 서비스 개발을 위해 Django에서 미리 만들어둔 선물이니까요.

Django에서는 프레임워크 사용자들의 빠른 웹 개발을 위해 다양한 모듈들을 지원하고 있고,
탄탄한 User 모델과 인증(Authentication), 인가(Authorization) 시스템도 그 중 하나 입니다.
*(보통 해당 기능들을 총괄하여 Django Authentication System 이라고 칭합니다.)

그리고 이 Authentication System은 위에서 생성된 기본 테이블들과 매우 밀접한 관계로 동작하고 있습니다.

이에, 이번 시리즈에서는 기본 생성 테이블들과 Django Authentication System의 연관성, 동작 방식, 실전 사용에 대해서 소개해드리려 합니다.

하지만 이 글 한 곳에 모두 설명했다간, 포스트가 infinity scroll(?)이 될 수 있으니 😨
이번 글에선 기본 User 모델인 auth_user부터 차근차근 알아보려 합니다.

User 모델 기본 필드 분석

우선 User 모델의 구조부터 함께 보겠습니다.
*(장고 공식 문서 일부를 직역 및 의역 하였습니다. 원문은 https://docs.djangoproject.com/en/3.2/ref/contrib/auth/ 를 참조해 주세요)


username (유저 명)_
필수, 최대 길이 150자.
알파벳과 숫자, 언더스코어(_), 엣(@), 플러스(+), 닷(.), 대쉬(-)를 포함할 수 있음.

150 자의 최대 길이는 대부분의 경우를 만족할 겁니다. 만약 더 길게 설정해야 한다면 커스텀 유저 모델을 사용하세요. 만약 utf8mb4 인코딩이 적용된 MySQL을 사용하고 계시다면 최대 191자의 길이 까지도 설정이 가능합니다.

first_name (이름)
필수 아님 (blank=True), 최대 길이 150자

last_name (성)
필수 아님 (blank=True), 최대 길이 150자

email (이메일)
필수 아님 (blank=True)

password (비밀번호)
필수

장고는 비밀번호 평문을 그대로 저장하지 않고, PBKDF2 라는 기본 암호화 시스템을 제공하고 있습니다.
PBKDF2는 미 NIST에서 공인한 해쉬 컨테이너 알고리즘이며, 자세한 내용은 Naver D2의 안전한 패스워드 저장 을 참고하시면 더욱 빠르게 이해하실 수 있으실 것 같습니다.

groups (그룹)
"Group" 과 다대다 관계 (Many-to-Many relationship)

user_permissions (권한)
"Permission" 과 다대다 관계 (Many-to-Many relationship)

is_staff (스태프 여부)
Boolean 값. ( 최초 가입 시 Default False )

is_active (활성 여부)
Boolean 값. ( 최초 가입 시 Default True )

is_superuser (슈퍼유저 여부)
Boolean 값. ( 최초 가입 시 Default False )

last_login (마지막 로그인 일자)
DATETIME

date_joined (계정 생성일자)
DATETIME


장고 기본 유저 모델은 이렇게 위 구조로 이루어져 있습니다.

groupsuser_permissions 필드를 보시고 "어? 내 테이블엔 없는데?" 라고 걱정하지 않으셔도 됩니다.
두 필드의 내용은 실제 테이블 내 필드가 아닌, 브릿지 테이블에서 이루어지는 다대다 관계를 설명하기 위함입니다.

그럼 브릿지 테이블은 어딨냐! Group은 뭐고 Permission은 또 뭐냐! 하실 수 있는데, 이번 시리즈의 다음 글에서 더 자세히 설명드릴 예정이니 너무 걱정하지 마시고 이번 글에선 보류하도록 하겠습니다.

근데 위 두 필드를 제외하더라도 is_active, is_staff, is_superuser 이 세 필드의 용도가 혼란스럽습니다 !

해당 세 필드는 "유저 개인 권한"을 명시하기 위한 필드이며, 장고 공식문서에서 해당 필드에 대해 추가로 설명하는 바는 아래와 같습니다.

is_active: 유저의 활성 여부를 판단하기 위한 필드입니다.

계정을 지워야 할 일이 있을 때 실제 삭제보다는 이 필드 값을 False로 돌리는 것을 추천합니다. 삭제 시점에 서비스에 이미 이 기본 User 모델을 참조하는 외래키가 있을 시 영향을 끼치게 하지 않기 위함 입니다.

is_active 필드로 유저의 로그인 허용 여부를 항상 판단할 필요는 없습니다.
기본 backend인 ModelBackend와 RemoteUserBackend는 is_active 필드를 통해 로그인 허용 여부를 판단하고 있지만, 활성 여부(is_active)와 상관 없이 모든 유저의 로그인을 허용하고 싶다면 AllowAllUsersModelBackend 혹은 AllowAllUsersRemoteUserBackend를 사용하여 비활성 유저의 로그인을 허용할 수 있습니다.

이 경우, LoginView에서 사용되는 AuthenticationForm 또한 추가 커스터마이징이 필요합니다.

또한 has_perm() 등의 권한 체크 함수나 Django Admin 관련 인증은 비활성 유저에게 항상 False만 리턴하도록 되어있으니 주의하세요.

💡 LoginView를 사용하지 않으신다면 Backend만 변경하여도 무관합니다

Q. 왜 LoginView를 사용하면 Backend를 변경하였음에도 불구하고 AuthenticationForm을 추가 커스터마이징 해야 하나요?

( django/contrib/auth/views)

우선 위 사진과 같이 장고 LoginView의 기본 Form은 AuthenticationForm 입니다.


( django/views/generic/edit)

다음으로 LoginView의 부모 클래스인 ProcessFormView를 보겠습니다.
POST 요청 시 form에 대해 is_valid를 진행하게 되는데, is_valid 진행 과정에서 form 객체 (현재 AuthenticationForm) 의 clean을 진행하게 됩니다.

*( 장고의 Form Validation에 대해 보다 자세한 내용이 궁금하시다면 공식 문서를 참고해 주세요. )


( django/contrib/auth/forms)

AuthenticationForm의 clean 메서드 입니다.
사진에서 보실 수 있으시듯이, authenticate에서 통과되더라도 아래 confirm_login_allowed 에서 유저가 is_active가 아니면 ValidationError가 발생하도록 되어있습니다.

이 때문에 Backend를 변경해서 authenticate를 통과하더라도, LoginView를 사용하게 되면 AuthenticationForm에 대한 추가 커스터마이징이 필요한 것입니다.

is_staff: 유저의 admin 사이트 접근 허용 여부를 판단하기 위한 필드입니다.

admin 사이트 내에서의 추가적인 행동(특정 모델에 대한 CRUD)은 별도 Permission을 할당 받아야만 가능합니다.

is_superuser: 유저의 슈퍼유저 여부를 판단하기 위한 필드입니다.
슈퍼유저는 별도의 Permission 할당 없이도 admin 사이트 내에서의 모든 행동 권한을 부여 받습니다



위와 같이 auth user 의 해당 세 필드 또한 로그인, 어드민과 깊은 연관성이 있습니다.
*( 혹 장고 어드민을 사용하시지 않으시거나 추후 Group & Permission을 이용하실 생각이 없으시더라도
단순 별도 권한 체크 등 다른 용도로 충분히 사용하실 수 있으실 거예요. )

이처럼 superuser는 별도의 권한 없이도 admin 사이트에서 모든 모델을 대상으로 자유롭게 CRUD를 할 수 있습니다.

많은 장고 튜토리얼에서 createsuperuser를 하는 이유도, admin 사이트에서 자유자재로 모델에 대한 변경이 가능하기 때문에 여러분들의 원활한 장고 학습을 위함이었다는 것이죠 !

현업에서 여러 명의 개발자들과 장고 기반 웹을 개발하게 되신다면, 실제 프로덕션 환경의 admin에서 모든 개발자에게 superuser를 할당하는 것은 상당히 위험할 수도 있다는 점을 인지하고 계시면 좋을 것 같습니다.

+(보통은 프로덕션 환경에서 보안 상 admin 사이트 자체를 비활성화 해놓긴 합니다만, 모든 환경이 동일하진 않으니 혹시나 하는 마음에 언급했습니다. 프로덕션 레벨에서의 보안에 대해서는 역량이 된다면 추후 별도의 글로 작성할 예정입니다.)

여담 : last_login은 누가 언제 업데이트 하는가 ?!

User 모델 내의 last login 필드는 정확히 어느 시점에 갱신되는지 가볍게 확인해보겠습니다.

( django/contrib/auth/__init__ -> login())


( django/contrib/auth/models)

로그인 시 사용되는 login() 함수의 최하단에 user_logged_in Signal을 이용하여 models의 update_last_login을 호출하고 있습니다.

이렇게 login 함수를 사용할 때 마다 last_login 필드가 자동으로 업데이트 된다는 것을 알 수 있습니다.


마무리

이번 글에서는 장고의 기본 User 모델에 대해 알아보았습니다.

" 앗 잠시만요 ! 저희 서비스의 유저 정보는 기본 User 모델로는 턱없이 부족해요 ! 이대로 끝인가요? "

맞습니다. 위에서 보셨듯 장고의 기본 User 모델 필드들은 보통의 서비스들에서 수집하는 많은 유저 정보들에 비하면 턱없이 부족합니다.

하지만 걱정 마세요, 그런 이유로 장고에서는 이미 기본 User 모델에 대한 여러가지 확장 방법을 제공하고 있습니다.

이에 시리즈 다음 글은 기본 User 모델 확장하기 를 주제로 포스팅 할 예정입니다.

요새 개인적인 일들이 많아 글을 자주 올리기는 어렵지만 역량이 닿는 대로 연재를 해 볼 생각입니다. 😅

아마 글이 자주 올라오진 못하더라도 해당 시리즈는 2021년 하반기 내에는 마무리 할 생각이니, 가끔 생각 나실 때 들러주세요 ! 😄

많이 부족한 첫 시리즈이지만 장고의 인지도가 점점 증가하는 요즈음, 아직은 낯선 장고를 처음 접하는 분들께 조금이나마 도움이 되었으면 하는 바램입니다.

부족한 글 읽어주셔서 감사드리며, 잘못되거나 부족한 내용을 발견하셨다면 언제든지 날카롭게 지적해주시면 즉시 수정토록 하겠습니다. 감사합니다 !

profile
세상을 바꾸진 못 하더라도, 지나간 자리는 더욱 깔끔하게 만드려고 노력하는 보이스카웃 단원입니다.

0개의 댓글