[DRF] 로그인과 회원가입

송민준·2023년 3월 17일
0

출그니 (캡스톤)

목록 보기
1/6

목표는 다음과 같은 필드를 가진 User 테이블을 생성하는 것이다.

현재 개발 프로젝트 폴더

python [manage.py](http://manage.py) startapp 을 통해 authentication app을 생성했다.

serializers.py

serializers의 역할은 직렬화와 역직렬화라고 한다.

나는 다음과 같은 의도로 사용했다.

  1. 유저가 보낸 request 를 우리가 정의한 모델과 맞아떨어지는지 체크 (is_valid)
  2. 그 request 대로 테이블에 저장 (save)
  3. User 모델의 인스턴스는 class User 형식인데 이걸 {"user_id": "admin", "password", ...} 이런식으로 직렬화.

이 3가지는 다음에 views.py에서 설명함.

from .models import User
from rest_framework import serializers

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = "__all__"
  • class Meta : 이 클래스는 serializer가 작동하기에 필요한 동작을 명세하는 곳
    • model = User 이 serializer가 User 모델을 따른다는 뜻
    • fields = “__all__” : User의 모든 필드가 직렬화에 필요함. 추가적으로 다른 모델과 중첩으로 이 serializer 를 구성하고 싶다면 아래와 같이 하면 된다.
      class UserSerializer(serializers.ModelSerializer):
          profile = ProfileSerializer()
      
          class Meta:
              model = User
              fields = ['username', 'email', 'profile']
      
          def create(self, validated_data):
              profile_data = validated_data.pop('profile')
              user = User.objects.create(**validated_data)
              Profile.objects.create(user=user, **profile_data)
              return user
      • UserSerializer.data 결과
        • {”username”:”name”, “email”:”email.kookmin.ac.kr”, “profile” : {”profilenum”: “10”} }

models.py

model은 테이블에서 나타날 필드를 작성하는 곳.

작성하고

python [manage.py](http://manage.py) makemigrations

python [manage.py](http://manage.py) migrate

를 수행하면

mysqlworkbench에서 보았을 때 다음과 같이 테이블이 생성됨을 볼 수 있다.

from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from django.contrib.auth.hashers import make_password
# 이 클래스는 User에 대해 생성하는 과정을 담당한다. 
class UserManager(BaseUserManager):
    def create_user(self, user_email, password, **extra_fields):
        if password is None:
            raise TypeError("User must have a password")

        extra_fields.setdefault("is_admin", False)
        extra_fields.setdefault("is_staff", False)
				# User에서 정의한대로 인스턴스 생성
        user = self.model(
            user_email=user_email,
            **extra_fields
        )
        user.set_password(password)
				# .save 를 통해 db에 푸시.
        user.save(using=self._db)
        return user
		# 이 함수는 특수한 방법 아니면 호출될 수 없다. python manage.py createsuperuser 같은 경우.
    def create_superuser(self, user_email='admin.kookmin.ac.kr', password='', **extra_fields):
        if password is None:
            raise TypeError("SuperUser must have a password")
				
				# admin 임을 지정
        extra_fields.setdefault("is_admin",True)
        extra_fields.setdefault("is_staff",True)

        return self.create_user(user_email=user_email, password=password, **extra_fields)

# Create your models here.

class User(AbstractBaseUser):
		# sqldbm (맨위 사진) 대로 필드 만들기
    user_email = models.CharField(max_length=30)
    user_nickname = models.CharField(max_length=10, unique=True)
    user_definition = models.CharField(max_length=100, null=True)
    user_profile_image = models.CharField(max_length=200, null=True)
    user_point_number = models.IntegerField(default=0, null=True)
		# 이 유저가 스텝인지 어드민인지 boolean으로 구분
    is_admin = models.BooleanField(default=False)
    is_staff = models.BooleanField(default=False)
		#Usernamedmf user_nickname으로 취급한다. username과 password는 django에서 기본적으로 인증을 위해 필수적인 사항이므로 이렇게 지정해야한다.
    USERNAME_FIELD = "user_nickname"
    REQUIRED_FIELD = ['user_nickname']
		
		# 이 객체의 Manager 지정. 
    objects = UserManager()

    def save(self, *args, **kwargs):
        if self.password:
            self.password = make_password(self.password)
        super().save(*args, **kwargs)

django에는 기본적으로 User 모델이 있지만 우리가 원하는 User 모델은 추가적인 필드가 필요하므로 AbstractBaseUser를 상속해서 재정의했다.

urls.py

from django.urls import path, include
from . import views
from .views import UserSignupView, UserLoginView, UserInfoView

urlpatterns = [
    path('signup/', UserSignupView.as_view(), name="user_signup_view"),
    path('login/', UserLoginView.as_view(), name="user_login_view"),
    path('info/', UserInfoView.as_view(), name="user_info_view")
]

database/urls와는 다른 authentication/urls 이다.

database/urls는 루트 경로를 정의하고 루트 경로에서 user/ 는 이 authentication/urls 를 따른다고 작성했기 때문에 [http://127.0.0.1:8000/user/](http://127.0.0.1:8000/user/) 으로 시작하는 경로는 위와 같은 urlpatterns를 따른다.

  • .../signup/: UserSignupView를 실행한다.
  • .../login/:UserLoginView를 실행한다.
  • .../info/:UserInfoView를 실행한다.

views.py

http://127.0.0.1:8000/user/signup

이런식으로 인터넷 주소로 들어갔을 때 여기 views.py 의 클래스들을 이용하도록 urls.py에 작성했다.

from rest_framework.authtoken.models import Token
from rest_framework import permissions, status, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.authentication import TokenAuthentication
from .serializers import UserSerializer
from .models import User
from django.contrib.auth import authenticate

#user/signup/
class UserSignupView(APIView):
		# User.objects.all() : User 모델의 모든 객체를 조회하는 쿼리
    queryset = User.objects.all()
    serializer_class = UserSerializer
    def post(self, request):
        serializer = UserSerializer(data=request.data) # serializer에 data 적용
        data = {} # 이 딕셔너리는 return Response(data)를 통해 요청한 유저에게 제공될 정보. Response 함수는 json 변환을 지원하나보다.
        if serializer.is_valid(): # serializer가 validation 수행
            user = serializer.save() # db에 저장
            data['user_nickname'] = serializer.data['user_nickname']
			
        else: # 오류
            data = serializer.errors
            return Response(data, status=status.HTTP_400_BAD_REQUEST)
        return Response(data)
#user/login/
class UserLoginView(APIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    def post(self, request, *args, **kwargs):
        user_nickname = request.data["user_nickname"]
        password = request.data["password"]
				# authenticate : username과 password를 통해 User 테이블의 정보와 맞는지 검색. 
        user = authenticate(user_nickname=user_nickname, password=password)
        if user:
            token, created = Token.objects.get_or_create(user=user) # .get_or_create : 유저에 해당하는 컬럼에 이미 토큰이 있으면 get  없으면 create 후 get
            # 토큰은 별도의 테이블에 저장됨
						return Response({"token": token.key})
        else:
            return Response(status=status.HTTP_400_BAD_REQUEST)
#user/info/
class UserInfoView(APIView):
    # Token 으로 유저의 정보를 탐색
    def post(self, request):
        token = request.data["token"]
        user_id = Token.objects.filter(key=token).values().first()["user_id"]
        user_info = User.objects.filter(id=user_id).values().first()
        return Response(user_info)
  • authtoken_token : 토큰이 저장된 테이블 (django에서 별도로 제공)

테스트

python [manage.py](http://manage.py) runserver 이후

UserSignupView

web

[http://127.0.0.1:8000/user](http://127.0.0.1:8000/user/)/signup 접속

mysqlworkbench

(순차적으로 id,password, last_login, user_email, user_nickname, user_definition, user_profile_image, user_point_number, is_admin, is_staff)

UserLoginView

web

[http://127.0.0.1:8000/user](http://127.0.0.1:8000/user/)/login 접속

mysqlworkbench

UserInfoView

[http://127.0.0.1:8000/user](http://127.0.0.1:8000/user/)/info 접속

profile
개발자

0개의 댓글