[Django] 4. User 모델 생성(3) - View

안녕하세요! 레오입니다🤗 최근 코로나 바이러스가 다시 전국적으로 유행하고 있네요. 모두들 건강관리 유의하시며 지내시길 바랍니당! 이럴 때에는 집에서 콕 박혀서 마이티마우스 노래 크게 틀어놓고 코딩하는게 또 행복 아니겠습니까 ㅎㅎ 오늘은 User 모델 생성을 주제로 한 포스팅을 마무리하려고 합니다. 회원가입 API View를 작성하면서 끝마칠 생각이구 기타 settings.py와 urls.py도 업데이트 하겠습니다. 바로 출발하겠습니다.🚂🚂

View를 구현하는 방식

View를 구현하는 방식에는 def를 이용하는 함수형 방식과 class를 이용하는 클래스형 방식이 있습니다.

Function Based Views(FBV)

Saying [that class-based views] is always the superior solution is a mistake.
— Nick Coghlan

처음에는 class가 되게 심오하고 딱딱하게만 느껴졌고 공부를 시작할 때에는 View를 만들 때 함수형을 사용하였습니다. 매우 직관적으로 코드를 작성할 수 있어서 처음에 학습하기도 좋고 코드가 상대적으로 가볍기때문에 큰 프로젝트에서도 단순한 API를 구현할 때 이 함수형 방식을 사용한다고 합니다.
기본적인 방식은 데코레이터(@)를 이용하여 요청을 구분하고 httprequest를 받는 대신에 request의 instance를 받습니다. 그리고 함수 내부에서 if문을 이용하여 request의 method를 파악하고 그에 따라 return을 다르게 할 수 있습니다. 다만 GET과 POST에 대해서만 응답이 가능하여(여러 if문과 코드로 수정과 삭제를 할 수 있긴 합니다.) 기능이 제한적입니다.

Class Based Views(CBV)

Django's class-based views are a welcome departure from the old-style views.
— Reinout van Rees

위와 같이 FBV의 제한적인 기능때문에 class의 필요성을 느끼고 클래스형 방식의 뷰를 공부하기 시작하였습니다. CBV의 가장 주요한 장점은 재사용성이 높은 메소드들을 오버라이딩 하거나 결합하여 입맛에 맞게 뷰를 작성하게끔 하는 것입니다. DRF는 자주 사용되는 패턴의 메소드들을 미리 작성하여 이를 실현합니다. 클래스형 방식의 View에도 여러 계층이 존재합니다.

APIView -> GenericAPIView -> Concrete View Classes -> ViewSets

이처럼 크게 4개의 계층이 존재하고 화살표 방향으로 상속받습니다.

  1. APIView
    가장 기본이 되는 View입니다. 해당 뷰에서는 authentication_classes와 permission_classes를 default로 사용할 수 있습니다. 다만 모든 요청들을 직접 작성하여야 하므로(default로 작성된 요청이 없습니다) 단독으로 사용하는 경우는 드뭅니다.
    👉 APIView 코드 문서 보러가기

  2. GenericAPIView
    우리가 사용할 기능들을 가지고있는 가장 최소한의 뷰입니다🤟 queryset을 정의하고 serializer를 사용할 수 있습니다. 또한 url을 통해 받은 parameter를 lookup할 수도 있고 페이지네이션 기능을 추가할 수도 있습니다! 다만 GET, POST등 요청에 대해 구체적인 실행 코드가 없어 이러한 요청을 커스터마이징할 때 일부 사용되고 평상시에는 부모님👪의 마음과 같이, 자식 클래스를 위해 존재합니다.

  3. Concrete View Classes
    가장 많이 사용되는 뷰 클래스입니다(사실 현업을 한 적이 없어서 정확하지 않아용. 메소드들이 가장 효율적으로 제공되어있고 구글링할 때 사용빈도가 높고 검색결과가 가장 많은 뷰였기에 이렇게 판단했습니다😉😉). 인자한 부모님(GenericAPIView)으로 부터 많은 기능들을 상속받은 기능 금수저🍴라고 할 수 있고, Mixin(클래스에 부가적인 기능이나 정보를 추가해주기 위한 모듈)으로부터 각각의 요청에 해당하는 기능을 추가적으로 상속받아 내실이 쌓인 뷰 클래스입니다. 'CreateAPIView', 'ListAPIView', 'UpdateAPIView' 등 모든 http 요청에 대해 뷰가 존재하고 'ListCreateAPIView'와 같이 한 화면에서 여러 요청을 처리할 수도 있습니다.
    👉인기 만점, Generics과 아이들 코드 문서

  4. Viewsets
    Viewsets은 말 그대로 위의 뷰들을 모두 한 클래스안에 몽땅 다 담은 뷰 클래스입니다. 다만 다른 점은 'get', 'post'와 같이 요청으로 함수들이 정의되는 것이 아니라 'list', 'create' 등 action으로 함수들을 정의합니다. 또한 urls.py에서 쓰일 router와 함께 사용합니다.
    👉Viewsets 코드 문서 보러가기
    저는 하나씩 파헤쳐보고 싶기도 했고, 아직 Viewsets을 사용할 만한 내공도 쌓이지 않아서 Concrete View Classes를 사용하기로 했습니다!

회원가입 View

기본적인 구조는 다음과 같습니다.

  • 프론트 서버로부터 email, password, nickname이 담긴 json 데이터를 전달받습니다.
  • serializer를 거친 뒤 에러가 나면 에러 코드를, 저장이 되면 성공 응답을 반환합니다.
# views.py

from django.shortcuts import render

from rest_framework import status
from rest_framework.generics import CreateAPIView
from rest_framework.response import Response
from rest_framework.permissions import AllowAny

from .serializers import UserRegistrationSerializer
from .models import User

class UserRegistrationView(CreateAPIView):
    serializer_class = UserRegistrationSerializer
    permission_classes = (AllowAny,)

    def post(self, request):
        serializer = self.serializer_class(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        status_code = status.HTTP_201_CREATED
        response = {
            'success': "true",
            'status code': status_code,
            'message': "user registered successfully",
        }
        return Response(response, status=status_code)
  • serializer_class: serialize할 serializer를 정의합니다.
  • permision_classes: 접근 허용 범위를 정의합니다. default가 AllowAny지만 직관적으로 보이기 위해 다시 정의하였습니다.
  • self.serializer_class: 정의했던 시리얼라이저를 이용하여 인스턴스로 만들어줍니다. serializer의 모습은 다음과 같습니다.
UserRegistrationSerializer(data=<QueryDict: {'csrfmiddlewaretoken': [], 'email': [], 'password': [], 'nickname': []}>)

미들웨어로부터 받은 csrf_token도 탑승해있는 것을 확인할 수 있습니다.

  • is_valid(): 받은 데이터를 이용하여 validation을 수행합니다. validation에서 통과하면 True를 반환하고 serializer 인스턴스의 validated_data에 데이터가 담깁니다. 통과하지 못하면 Error를 반환합니다.
    👉is_valid() 더욱 자세히 파헤치러 가기
  • save(): serializer 내부의 create()함수를 호출합니다. create()함수는 지난 시간 만들었던 회원가입 serializer에서 오버라이딩하여 정의하였습니다.
    👉회원가입 serializer 보러가기
    HTTP_201_CREATED: 성공적인 생성을 뜻하는 201 코드를 반환합니다.

코드들을 보면 serializers.py의 회원가입 Serializer와 매우 유기적으로 연결되어 있는 것을 확인할 수 있습니다! 작성된 코드를 그대로 사용하기보다는 하나씩 뜯어보고 찾아가면서 이해를 하려고 노력하였습니다.

이제는 간소하지만 매우 중요한 부분을 수정하겠습니다. settings와 urls 인데용🏄 복잡한 부분을 마무리했으니 즐거운 마음으로 들어가봅시당😆

Setting과 URL

# myproject/settings.py

...

INSTALLED_APPS = [
    
    ...
    
    # model
    'user',

    # module
    'rest_framework',
]

...

AUTH_USER_MODEL = 'user.User'

INSTALLED_APPS는 만든 모델, 설치한 모듈 등 여러 앱들을 명시하는 곳입니다. 모델을 만들거나 모듈을 설치한 뒤 INSTALLED_APPS에 추가하는 것을 자주 까먹는데, 추가하지 않으면 반드시 에러가 나기때문에 꼭 기억해주시기 바랍니다!😂😂 또한 AUTH_USER_MODEL은 장고에서 기본으로 간주하는 유저 모델을 설정하는 것인데, USER 모델을 따로 커스터마이징해서 만들었으니 이 부분을 만들었던 USER 모델로 바꾸어줍니다.

# myproject/urls.py

from django.contrib import admin
from django.urls import path, include

from user import urls

urlpatterns = [
    path('admin/', admin.site.urls),
    path('user/', include('user.urls')),
]

myproject/urls.py는 인덱스화면, 홈화면인 8000포트 바로 뒤를 바라보는 최상위 url 경로입니다. include 메소드를 이용하여 ':8000/user/'로 오는 모든 요청을 user.urls로 가도록 설정해줍니다.

from django.conf.urls import url
from django.urls import  path

from .views import UserRegistrationView

urlpatterns = [
    path('signup/', UserRegistrationView.as_view()),
]

이제 마지막입니다. user/ 뒤를 바라보는 url 경로입니다. user/signup/으로 요청이 오면 만들어놨던 UserRegistrationView의 as_view()를 실행시키면서 화면으로 저장되어있는 template을 띄웁니다.

이제 만들었던 파일들을 모두 저장하고 runserver를 돌리기 전에 해야할 일이 있습니다. 지금까지 만들었던 모든 앱/모델들을 db와 장고에 알려서 저장시켜야 하는데 이를 migration이라고 합니다. 콘솔창에서 manage.py가 있는 경로로 맞춘 뒤 아래의 명령어를 실행시킵니다.

(django_venv)$ python3 manage.py makemigration
(django_venv)$ python3 manage.py migrate

makemigration은 모델을 디비에 저장하는 과정이고 migrate는 makemigration을 통해 저장된 디비와 INSTALLED_APPS에 추가된 앱들을 장고에 알려주는 역할을 하는 것으로 보면 됩니다. 이제 만들어진 화면을 한번 보겠습니다~!!
runserver 명령을 실행한 뒤 '127.0.0.1:8000/user/signup/'으로 들어가면

이와 같은 화면을 만날 수 있습니다. 만든 뷰는 CreateAPIView를 상속받았기 때문에 GET 요청은 405에러를 반환하고 not allowed라고 알려줍니다. 회원가입을 위해 email과 password, nickname칸에 각각의 타입에 맞게 쓴 뒤 POST를 요청하면,

모든 과정을 성공적으로 끝마친 뒤 만들어놨던 인스턴스 그대로 반환해주었습니다!👏👏

드디어 처음 만들어본 API인 회원가입 API를 모두 포스트하였습니다. 뿌듯함도 잠시, 아직도 코드 문서에 들어가보면 고약하게 생긴 코드들이 너무 많아서 울적해지기도 하는데요, 그래도 만들고자 예정했던대로 구현이 되어서 흐뭇함은 감출 수가 없습니다🏆🏆 다음 포스트에서는 로그인 API를 구현하겠습니다. 긴 글 읽어주셔서 감사합니다😃😃

좋은 웹페이지 즐겨찾기