프롤로그
장고 책 2권을 보면서 페이지 만드는 작업을 하였었는데, API라든지, JSON 형식이라든지 이런 기능을 설명해주는 책은 없었고, 스스로 알아내가야했다. 그리고...이 작업에 관련된 자료들이 진짜 매우매우 없다;;
그래서 일단 내가 따로 찾아보면서 공부한 내용들을 정리해보고자 한다.
살짝 개념 정리 몇 개를 해보자면
DRF
Django Rest Framework 의 약자로, Django를 위한 강력하고 유연한 웹 API 구축을 위한 오픈소스 프레임 워크이다.
DRF는 직렬화(Serialization) , 인증(Authentication) 및 권한(Permission), 브라우저블 API, ORM및 비ORM 데이터 소스 지원, 클래스 기반 뷰의 특징을 가지고 있다.
직렬화의 경우, 파이썬 데이터 파입을 JSON, XML 같은 콘텐츠 타입으로 쉽게 변환 할 수 있게 해주는 직렬화 기능을 제공하는데, 나중에 프론트엔드랑 작업할 때, 이 직렬화 하여서 프론트엔드에게 GET 요청일 때 이 직렬화된 정보를 보내주고, POST요청일 때 직렬화된 정보를 받는 식으로 작업을 하게 된다.
클래스 기반 뷰는 Django의 클래스 기반 뷰를 활용하여 API 뷰를 구조화 하고 재사용성을 높일 수 있다.
되게 편하고, 유지보수성을 향상 시킬 수 있다. 이정도만 알면 될 것 같다.
API
API(Application Programming Interface) 로써, 소프트웨어나 어플리케이션 간에 서로 정보를 교환하고 기능을 사용할 수 있도록 하는 일련의 규칙, 프로토콜, 도구의 집합이다. 한 프로그램을 다른 사람들이 쉽게 사용할 수 있도록 구현해 놓았다고 생각하면 된다. 우리는 그냥 바로 쓸 수 있게 하는 것? 그래서 여기서 데이터 요청할 수 있는 방법을 정의해놓으면 우린 그대로 보내줘서 사용할 수 있다.
API의 주요 장점은 추상화인데, API를 사용하면 복잡한 내부 구현을 몰라도 해당 기능을 사용할 수 있다. 그래서 모듈성, 재사용성, 효율성을 크게 향상시킨다.
이제 실제 회원가입을 구현해보자.
아 맞다 그전에 파이썬 가상환경에서
pip install djangorestframework
이걸로 drf를 깔자
시리얼라이저 정의하기
그동안 우리는 막 템플릿 만들면서 forms.py 작성하고 뭐 이것저것 해서 이상한 곳에 시간을 쏟았는데 이제 그럴 필요 없다. 어차피 우린 프론트엔드랑 작업하는 거고, 프론트 측에서 관리해야할 것이기에 우리는 회원가입시 필요한 데이터를 "직렬화" 해서 넘겨주면 되는 것이다.
우선 내가 작업하고자 하는 디렉터리 안에 serializers.py 를 만들자.
일단 settings.py에서
AUTH_USER_MODEL = 'signup.User'
이 코드를 아무대나 붙여넣어주자. login은 내가 현재 작업하는 app이름이고, User 는 사용자 객체이다.
프로젝트 전체에서 사용할 사용자 모델이 저거라고 알려주는 것이다.
그다음 아까만큰 시리얼라이즈.py로 가자.
이는 사용자 모델에 대한 시리얼라이저(직렬화)를 정의하는 곳이다. 이 기능은 모델 인스턴스를 JSON으로 변환하건, JSON 데이터를 파싱하여 모델 인스턴스로 변환하는 것이다.
# serializers.py
from rest_framework import serializers
from .models import User, Individual, Enterprise
class EnterpriseSerializer(serializers.ModelSerializer):
class Meta:
model = Enterprise
fields = ('company_name', 'department', 'position')
class IndividualSerializer(serializers.ModelSerializer):
class Meta:
model = Individual
fields = ('school', 'department')
class UserSerializer(serializers.ModelSerializer):
individual = IndividualSerializer(required=False)
enterprise = EnterpriseSerializer(required=False)
class Meta:
model = User
fields = ('email', 'name', 'password', 'phone', 'birthdate', 'addr', 'sex', 'user_type', 'nickname', 'individual', 'enterprise')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
individual_data = validated_data.pop('individual', None)
enterprise_data = validated_data.pop('enterprise', None)
user = User.objects.create_user(**validated_data)
if user.user_type == 'i' and individual_data:
Individual.objects.create(user=user, **individual_data)
elif user.user_type == 'e' and enterprise_data:
Enterprise.objects.create(user=user, **enterprise_data)
return user
우선 UserSerializer부터 보자.
- Meta 내부 클래스에서 시리얼라이저가 사용할 모델(User)과 필드를 정의
- fields 튜플은 시리얼라이저가 포함할 User 모델의 필드를 지정
- extra_kwargs 사전을 사용하여 추가 옵션을 지정한다.. 여기서 'password' 필드는 {'write_only': True} 옵션을 가짐으로써, 시리얼라이즈된 출력에서 제외. 이는 비밀번호가 응답 데이터에 포함되지 않도록 하기 위함이다.
이렇게 작성하고 밑에 def create 부분에서 검증된 데이터 (validated_data)를 받아 새로운 User 인스턴스를 생성하고 저장한다. 그리고 최종적으로 생성된 사용자 객체를 반환한다.
직렬화에서 왜 create 가 사용되나...의문점이 생길 수 있다.
DRF가 데이터의 직렬화 뿐만 아니라 해당 데이터를 기반으로 모델 인스턴스를 생성하고 저장하는 과정까지 처리하기 때문이다. 직렬화는 단순히 데이터를 JSON형식으로 변환하는 과정을 넘어, 클라이언트로부터 받은 데이터를 검증하고, 해당 데이터를 사용하여 데이터베이스에 새로운 레코드를 생성하는 기능을 포함한다.
그래서 create 메서드는 is_valid() 메서드를 통해서 필드 유효성 검사, 필드별 제약 조건 검사 등을 수행하여 검증한다. 유효하다고 판단되면 create 메서드가 호출되어 검증된 validated_data 를 사용하여 모델 인스턴스를 생성하는데, 이 과정에서 save 메서드가 호출되어 새 레코드가 생성된다.
유저 등급이 i라면, individual 모델을 가져오고, e라면 enterprise 를 가져온다. 이것들은 위에 시리얼라이즈들이 되어있다.
일단 인스턴스만 생성을 해놓고 create할때 등급에 맞춰서 생성하는 것이기 때문에 추가적인 데이터베이스 낭비는 이루어 지지 않는다.
나머지 EnterpriseSerializer 나 이런 거는...그냥 직렬화 했다 정도만 생각하면 된다.
결론적으로 create 는 직렬화 과정의 일부로서 클라이언트로부터 받은 데이터를 바탕으로 모델 인스턴스를 생성하고, 데이터베이스에 저장하는 역할을 한다. 이는 RESTful API 를 통해 리소스 생성 요청을 처리할 때 필수적인 기능이다...
회원가입 뷰 작성
지금까지 내가 본 거로는 APIView와 ModelViewSet 이렇게 두 개가 있다.
APIView
DRF의 가장 기본적인 뷰 클래스. RESTfulAPI 를 구축하기 위한 가장 낮은 수준의 추상화를 제공
APIView를 사용하면 HTTP 메서드별로 (get, post, put, delete....) 요청을 처리하는 메서드를 직접 정의해야하나, 각 요청에 대한 세밀한 제어가 가능하다.
ModelVewSet
ModelViewSet은 APIView를 기반으로 더 높은 수준의 추상화를 제공하는 클래스로, 모델에 대한 CRUD(Create, Read, Update, Delete)연산을 처리하는데 필요한 액션들을 이미 구현하고 있다.
ModelViewSet은 queryset과 serializer_class 속성을 설정함으로써 작동하며, 이를 통해 어떤 모델을 대상으로 하고, 어떤 시리얼라이저를 사용할지 정의한다. 라우터와 함께 사용되어 URL을 자동으로 구성해줘서 API개발을 더욱 신속하게 할 수 있게 해준다.
APIView와 ModelViewSet 중에 골라야 한다면....
게시판 같이 수정/삭제/조회/생성 같이 간단한 작업들 CRUD 작업을 빠르게 구현해야할 때에는 ModelViewSet을 선택한다. 반복적인 작업을 최소화 하고, 빠른 개발을 가능하게 한다.
반변에 특정 API 엔드포인트에 대해 세밀한 제어가 필요하거나, 표준 CRUD 작업 이외의 복잡한 로직을 구현해야할 때 선택한다.
저번 웹개발을 작성할 때에는
2023.12.24 - [스펙업/멋쟁이사자처럼] - [홈페이지 제작] 장고 백엔드 API 설정 - 질문 기능
2023.12.25 - [스펙업/멋쟁이사자처럼] - [홈페이지 제작] 장고 백엔드 API 설정 - 답변 기능
여기서는 ModelViewSet을 이용해서 빠르게 구현했었다.
이번에 내가 맡은 부분은 회원가입/로그인 페이지이고, 회원가입 페이지에서 이메일 확인 같은 추가 기능이 들어가기에 APIView를 이용해야할 것 같다.
views.py 에 이 코드를 작성하자.
APIView를 상속받아 회원가입을 처리하는 API뷰를 작성하는 것이다.
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .serializers import UserSerializer
class CreateUserAPIView(APIView):
def post(self, request):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
user = serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
코드는...아까 작성한 시리얼라이저를 생성해주고 타당한지 검사하고, 생성하는 과정이다.
성공하면 201, 실패하면 400을 반환한다.
url 작성하기
이제 urls.py 파일에서 회원가입 API 뷰를 URL 경로와 연결한다.
기존 책에서 템플릿에서 작성했던 것과 비슷하게 흘러가지 않은가? url작성하고..함수 작성하면 그 url로 연결하면 함수 실행하는 거. 근데 이제 이 함수는 템플릿을 반환하는 게 아니라 직렬화한 JSON 형식을 반환하는 거지
from django.urls import path
from .views import CreateUserAPIView
urlpatterns = [
path('', CreateUserAPIView.as_view(), name='signup'),
]
경로 설정을 보는데, 새로운 CreateUserAPIView.as_view() 를 볼 수 있다.
이는 DRF에서 정의한 클래스 기반 뷰(Class-Based View, CBV)이다. 회원가입을 처리하기 위한 것이다.
APIView를 상속받아, HTTP POST요청을 통해 사용자 데이터를 받고 이를 처리하여 새로운 사용자를 생성해내는 것이다.
as_view()는 APIView로부터 상속받은 메서드로, 클래스 기반 뷰를 사용하기 위해 필요한 처리를 자동으로 수행하는 것이다.
뭐 어려운 건 집어치우고 그냥 저렇게 설정했다 정도만 기억하자.
일단 간단한 회원가입 구현은 끝났다. 매우 간단하게 하였고, 나중에 추가적인 기능들 이메일 인증이라던지 이런 거는 일단 나중에 넣을 예정이다.
이제 테스트를 해봐야겠지?
+ 매우매우 큰 실수를 해서 코드 여기까지해놓고 싹 수정하느라 안 맞는 거 있을 수 있음.
회원가입 테스트
우선 저번에도 사용했던 POSTMAN을 사용할 것이다. 일단 자잘자잘한 오류들을 해결하고 서버를 연 다음(오류들 해결 못하겠으면 전지전능하신 챗지피티한테 물어봐라)
http://127.0.0.1:8000/signup/signup/
여기 url에다가 회원가입 모델거를 참고하여 정보들을 넣고 POST요청을 보내보자.
{
"email": "hey_minj@company.com",
"name": "기업 사용자",
"password": "1234",
"phone": "01012345678",
"birthdate": "1980-01-01",
"addr": "서울",
"sex": "M",
"user_type": "e",
"nickname": "company",
"enterprise": {
"company_name": "회사명",
"department": "부서명",
"position": "직책"
}
}
등록이 잘 된 것을 확인할 수 있다.
일반사용자도 잘 됐다.
이걸 이제 제대로 등록되었는지 확인하기위해서 admin에서 확인해보자
admin 코드 작성하기
from django.contrib import admin
from .models import User, Individual, Enterprise
# Register your models here.
class UserAdmin(admin.ModelAdmin):
list_display = ('email', 'name', 'phone', 'birthdate', 'addr', 'sex', 'user_type', 'nickname')
list_filter = ('user_type', 'sex')
search_fields = ('email', 'name', 'nickname')
class IndividualAdmin(admin.ModelAdmin):
list_display = ('user', 'school', 'department')
search_fields = ('user__name', 'school')
class EnterpriseAdmin(admin.ModelAdmin):
list_display = ('user', 'company_name', 'department', 'position')
search_fields = ('user__name', 'company_name')
# Register your models here.
admin.site.register(User, UserAdmin)
admin.site.register(Individual, IndividualAdmin)
admin.site.register(Enterprise, EnterpriseAdmin)
작성하고
localhost:8000/admin 으로 접속해서 확인해보면 잘 등록이 되어있다.
이상이다.
에필로그
일단 회원가입 시 POST 요청만 정의하였고, 실제 회원가입하는 부분만 구현되어 있다. 이제 테스트도 완료하였으니, 로그인 기능 및 JWT 를 알아보자.
(사실 이번글 쓰면서 회원가입 할 떄 JWT 같이 했다가 망해서 코드도 터졌었다;ㅜㅜ)
'웹 개발' 카테고리의 다른 글
[장고] JWT 사용하기 #4 (0) | 2024.02.10 |
---|---|
[장고] 장고로 JWT 사용하기 #3 (1) | 2024.01.31 |
[장고] 프로젝트 Model 작성하기 #1 (1) | 2024.01.29 |
[웹 개발] 개발환경과 서버환경 분리하기 (장고) (0) | 2023.12.24 |
[웹 개발] AWS Lightsail로 서버열기 (장고) (1) | 2023.12.24 |