본문 바로가기

TIL 및 WIL/TIL (Today I Learned)

[TIL] 2022.06.16 (Django 심화, DRF 2)

어제는 DRF와 데이터베이스에 대한 이론적인 공부를 진행하였다.

 

오늘은 VSCode에서 Django 프로젝트를 생성하고 DRF 모듈을 설치하면서 로그인과 회원가입 기능을 만들어 보았다.

 

우선은 VSCode에서 장고 프로젝트 파일을 만들어주기 전에 가상 환경(venv)을 먼저 만들어주었다.

다음과 같은 명령어를 터미널에 적어주면 가상 환경이 생성된다.

 

python -m venv [가상 환경 이름]

가상환경 venv

 

보기에도 가상 환경임을 알 수 있도록 가상 환경 명을 venv로 정해주고 생성해주었다.

그다음으로 생성된 가상 환경에 들어갈 수 있도록 터미널 창에 다음과 같이 적어주었다.

 

window 기준

 

필자는 Window 환경을 사용하고 있기 때문에 해당 사진과 같이 명령어를 입력해주었다.

 

아무런 명령어를 적어주지 않으면 (base) 위치에 있게 되는데, source ./venv/Scripts/activate 명령어를 통해 생성한 가상 환경에 들어간 것이다.

해당 가상 환경에서 나가고 싶다면 deactivate 명령어를 사용하여 빠져나오면 된다.

 

가상 환경 설치를 마치고 장고 프로젝트를 만들어주기 위한 장고 모듈을 다운로드해주었다.

 

pip install django

 

해당 명령어를 통해 장고 모듈을 설치해주었고, 추가로 이번에 배우기 위한 DRF 사용을 위한 모듈도 다운로드해주었다.

 

pip install djangorestframework

 

여기서 다른 사람들과 프로젝트를 같이 하는 상황이 생기면 설치한 모듈이 꼬이는 경우가 생길 수 있다.

그래서 해당 모듈의 통일성을 위해 requirements.txt라는 텍스트 파일을 만들어서 해당 텍스트 파일 내에 설치한 모듈들에 대한 정보들을 넣어주었다.

 

해당 모듈들의 정보를 넣어주기 위해서는 다음과 같은 명령어를 적어주면 된다.

 

pip freeze > requirements.txt

requirements.txt 파일 내에 적혀있는 모듈 정보

 

pip freeze 명령어는 설치한 모듈들을 기록해주기 위한 명령어이다.

requirements.txt 파일에 저장된 모듈들의 정보를 보고 다른 사람이 해당 모듈들을 설치하고 싶다면 다음 명령어를 사용하여 설치해주면 된다.

 

pip install -r requirements.txt

 

그리고 이후 장고 코드를 적어줄 장고 프로젝트와 해당 프로젝트 내에서 사용할 앱들을 생성해주었다.

 

django-admin startproject [프로젝트명] .

django-admin startapp [앱 이름]

 

장고 프로젝트 이름은 drf_project로 만들어 주었고, 생성해준 앱은 user와 blog를 생성해주었다.

그리고 생성된 장고 프로젝트의 settings.py 안에 INSTALLED_APPS에 생성된 앱들을 추가해주고, DRF 사용을 위한 코드도 추가해주었다.

 

settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'user',
    'blog',
]

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [ # 기본적인 view 접근 권한 지정
        'rest_framework.permissions.AllowAny'
    ],
    'DEFAULT_AUTHENTICATION_CLASSES': [ # session 혹은 token을 인증 할 클래스 설정
        'rest_framework.authentication.TokenAuthentication',
        'rest_framework.authentication.SessionAuthentication'
    ],
    'DEFAULT_PARSER_CLASSES': [ # request.data 속성에 액세스 할 때 사용되는 파서 지정
        'rest_framework.parsers.JSONParser',
        'rest_framework.parsers.FormParser',
        'rest_framework.parsers.MultiPartParser'
    ]
}

 

이후 python manage.py createsuperuser로 admin 계정을 만들어주고 해당 admin 계정으로 확인해주기 위해 user앱의 models.py, views.py, urls.py 내에 로그인과 로그아웃에 관련된 내용을 적어주었다.

 

user/models.py

from django.db import models
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser


# Create your models here.
# custom user model 사용 시 UserManager 클래스와 create_user, create_superuser 함수가 정의되어 있어야 함
class UserManager(BaseUserManager):
    def create_user(self, username, password=None):
        if not username:
            raise ValueError('Users must have an username')
        user = self.model(
            username=username,
        )
        user.set_password(password)
        user.save(using=self._db)
        return user
    
    # python manage.py createsuperuser 사용 시 해당 함수가 사용됨
    def create_superuser(self, username, password=None):
        user = self.create_user(
            username=username,
            password=password
        )
        user.is_admin = True
        user.save(using=self._db)
        return user


class User(AbstractBaseUser):
    username = models.CharField("사용자 계정", max_length=20, unique=True)
    email = models.EmailField("이메일 주소", max_length=100)
    password = models.CharField("비밀번호", max_length=20)
    fullname = models.CharField("이름", max_length=20)
    join_date = models.DateTimeField("가입일", auto_now_add=True)

    # is_active가 False일 경우 계정이 비활성화됨
    is_active = models.BooleanField(default=True)

    # is_staff에서 해당 값 사용
    is_admin = models.BooleanField(default=False)

    # id로 사용 할 필드 지정.
    # 로그인 시 USERNAME_FIELD에 설정 된 필드와 password가 사용된다.
    USERNAME_FIELD = 'username'

    # user를 생성할 때 입력받은 필드 지정
    REQUIRED_FIELDS = []

    # custom user 생성 시 필요
    objects = UserManager()

    def __str__(self):
        return self.username
    
    # 로그인 사용자의 특정 테이블의 CRUD 권한을 설정, perm table의 crud 권한이 들어간다.
    # admin일 경우 항상 True, 비활성 사용자(is_active=False)의 경우 항상 False
    def has_perm(self, perm, obj=None):
        return True

    # 로그인 사용자의 특정 app에 접근 가능 여부를 설정, app_label에는 app 이름이 들어간다.
    # admin일 경우 항상 True, 비활성 사용자(is_active=False)의 경우 항상 False
    def has_module_perms(self, app_label): 
        return True

    # admin 권한 설정
    @property
    def is_staff(self): 
        return self.is_admin


class UserProfile(models.Model):
    user = models.OneToOneField(to=User, verbose_name="사용자", on_delete=models.CASCADE)
    hobby = models.ManyToManyField(to="Hobby", verbose_name="취미")
    introduction = models.TextField("소개")
    birthday = models.DateField("생일")
    age = models.IntegerField("나이")

    def __str__(self):
        return f'{self.user.username}님의 프로필입니다.'


class Hobby(models.Model):
    name = models.CharField("취미", max_length=50)

    def __str__(self):
        return self.name

 

맨 위 상단의 class UserManager 부분은 Django에서 UserModel을 커스텀해서 만들 시 사용해줘야 한다고 정해준 것이다.

이번 모델에서는 장고에서 기본으로 제공하는 UserModel을 사용하는 것이 아닌 직접 만든 UserModel을 사용하기 때문에 해당 부분을 적어주었다.

 

user/views.py

from django.contrib.auth import login, logout, authenticate
from django.views.decorators.csrf import csrf_exempt
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import permissions
from rest_framework import status


class UserView(APIView):
    permission_classes = [permissions.AllowAny] # 누구나 view 조회 가능
    # permission_classes = [permissions.IsAdminUser] # admin만 view 조회 가능
    # permission_classes = [permissions.IsAuthenticated] # 로그인 된 사용자만 view 조회 가능

    # 로그인 기능
    @csrf_exempt
    def post(self, request):
        username = request.data.get('username', '')
        password = request.data.get('password', '')
        
        user = authenticate(request, username=username, password=password)
        
        if not user:
            return Response({"fail": "계정이 존재하지 않거나 패스워드가 일치하지 않습니다."}, status=status.HTTP_401_UNAUTHORIZED)

        login(request, user)

        return Response({"success": "로그인 완료"}, status=status.HTTP_200_OK)
    
    # 로그아웃
    def delete(self, request):
        logout(request)

        return Response({"success": "로그아웃 완료"}, status=status.HTTP_200_OK)

 

views.py 내에는 로그인과 로그아웃 기능을 담당하는 함수를 적어주었다.

이번 장고 프로젝트 파일에서는 CBV 방식을 사용하여 코드를 구현했기 때문에 get, post, put, delete 메소드들을 함수 형태로 사용해주었다.

 

그렇기에 로그인을 담당하는 post와 로그아웃을 담당하는 delete를 생성해주었다.

 

로그인과 로그아웃은 장고에서 지원을 해주기 때문에 login()과 logout()을 사용해주면 해당 기능을 구현할 수 있다.

 

user/urls.py

from django.urls import path
from user import views

urlpatterns = [
    # user/
    path('', views.UserView.as_view()),
]

 

views.py에 있는 클래스를 불러와서 해당 URL을 연결해주고 Postman을 통해 해당 URL에서 정상적으로 작동이 되는지 확인해주었다.

 

Postman에서 확인한 로그인 및 로그아웃

 

Postman에서도 정상적으로 잘 나오는 모습을 볼 수 있었다.

이후 정리된 내용은 Github에 올려두었다.

https://github.com/J1NU2/DRF_Practice

 

GitHub - J1NU2/DRF_Practice

Contribute to J1NU2/DRF_Practice development by creating an account on GitHub.

github.com

 

-

 

DRF를 공부하며 장고 프로젝트와 user 및 blog 앱을 만들고 코드를 적어주면서 로그인과 로그아웃을 만들어보았다.

이후 게시글에 관한 내용도 blog 앱에 추가해주면서 코드를 추가할 것 같다.

 

장고 로그인에 대한 것을 계속 반복해서 연습하다 보니 점차 익숙해지는 것 같다.

 

:P