[Django] Spring to Django : View 그리고 리뷰!

E woo·2024년 7월 4일

Django

목록 보기
3/3
post-thumbnail

Spring 의 Controller 와 Service 부분을 Django 에 맞게 바꿔보자!

View

Spring 의 경우 Service 와 Controller 를 나누어 비즈니스 로직을 따로 구분하였지만
우선 Django 에 익숙한 상태가 아니기 때문에 일단은 View 에 로직을 모두 구현하고

리팩토링을 하면서 분리해보자!

  • views.py
class FixExtensionView(APIView):
    def post(self, request, extension):
        extension_data = {'extension': extension, 'checked': True}
        serializer = ExtensionRequestSerializer(data=extension_data)
        if serializer.is_valid():
            extension_instance, created = Extension.objects.get_or_create(extension=extension)
            extension_instance.checked = True
            extension_instance.save()
            response_serializer = ExtensionResponseSerializer(extension_instance)
            return Response(response_serializer.data, status=status.HTTP_200_OK)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, extension):
        try:
            extension_instance = Extension.objects.get(extension=extension)
            extension_instance.checked = False
            extension_instance.save()
            response_serializer = ExtensionResponseSerializer(extension_instance)
            return Response(response_serializer.data, status=status.HTTP_200_OK)
        except Extension.DoesNotExist:
            return Response({'error': 'Extension not found'}, status=status.HTTP_404_NOT_FOUND)

    def get(self, request):
        extensions = Extension.objects.filter(checked=True)
        serializer = ExtensionResponseSerializer(extensions, many=True)
        return Response(serializer.data, status=status.HTTP_200_OK)

APIView 를 통해 HTTP 메서드에 따라 메서드 구현을 하고 메서드에 맞는 매핑이 가능하다.
(이와 비슷하게 @api_view [여기서는 @를 데코레이션이라고 부른다] 를 사용할 수도 있다 )

urls.py 설정

  • api/urls.py
from django.urls import path
from .views import FixExtensionView

urlpatterns = [
    path('api/fix-extension/', FixExtensionView.as_view(), name='fix-extension-list'),
    path('api/fix-extension/<str:extension>/', FixExtensionView.as_view(), name='fix-extension-detail'),
]
  • 프로젝트 root 폴더의 urls.py
from django.contrib import admin
from django.urls import path, include

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

INSTALLED_APPS 에러

위의 에러가 뜬다면 장고 앱을 만든 뒤 settings.py 에 만든 app 을 추가 하지 않았기 떄문이므로

이를 추가해준다.

정상적으로 서버 띄우기 성공!

CORS

여기서도 Spring 때와 마찬가지로 어김없이 CORS 에러가 발생한다

Django(DRF) CORS 정책 해결

is_vaild 오류

def post(self, request, extension):
    print(f"Received POST request with extension: {extension}")
    extension_data = {'extension': extension, 'is_checked': True}
    serializer = ExtensionRequestSerializer(data=extension_data)
    
    if serializer.is_valid():
        extension_instance = Extension.objects.get(extension=extension)
        extension_instance.is_checked = True
        extension_instance.save()
        response_serializer = ExtensionResponseSerializer(extension_instance)
        return Response(response_serializer.data, status=status.HTTP_200_OK)
        
    print(f"Serializer errors: {serializer.errors}")
    
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

위의 코드를 살펴볼 경우 그냥그냥 이상적으로 보인다.

extension 을 받아서 requestSerializer 를 통해 dto 형태로 만들고
만약 그 값이 유효하다면 (== is_vaild) 해당 extension 에 해당하는 값을 가져오고
check를 True 로 바꾸고 responseSerializer 에 맞게 반환한다.

그러나 위의 코드를 실행하고 위의 api 를 수행한다면

이러한 에러가 발생한다. 이미 존재하는 extension 이기 때문에 에러가 발생한다는 것이다.

분명 로직은 이상적인 것 같은데 왜일까?

그 이유는 다음과 같다.

is_vaild() 를 통해 생성한 serializer 를 유효한지 확인하는 작업은
해당 데이터가 데이터베이스에서 유효한지를 살펴보게 된다.

고정 확장자의 경우 이미 DB 에 해당 값들이 모두 저장되어 있는 상태이다!
(”고정” 이기 때문에 DB 설계 시 이미 테이블에 들어있다고 가정하였다.)

그러므로 만약 이미 bat 가 존재하는데 bat 가 DB 에서 유효하냐를 물어보게 된다면
당연히 에러가 발생한다. extension 값은 unique 하기 때문이다!!

따라서 이를 위해서는 is_vaild 를 통한 유효 검증 후 값 조회가 아닌 값 조회를 우선한 뒤
이후 경우에 대한 예외처리를 수행하는 것이 올바른 로직이 된다!!!!

def post(self, request, extension):
    print(f"Received POST request with extension: {extension}")
    try:
        # 이미 존재하는 extension 객체를 찾는다.
        extension_instance = Extension.objects.get(extension=extension)
        extension_instance.is_checked = True
        extension_instance.save()
        response_serializer = ExtensionResponseSerializer(extension_instance)
        return Response(response_serializer.data, status=status.HTTP_200_OK)
    except Extension.DoesNotExist:
        # 존재하지 않으면 새로운 객체를 생성한다.
        print(f"잘못된 고정 확장자 요청입니다.")
        return Response(response_serializer.data, status=status.HTTP_400_BAD_REQUEST)
        print(f"Serializer errors: {serializer.errors}")
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

try except 를 통해 처리를 하게 되고 위의 설명 과정 처럼
먼저 extension 에 대한 조회를 해보고 존재하는 경우 (정상 동작)
만약 존재하지 않는 extension 에 대해서는 에러를 내보낸다.

(단순히 4xx 이 아닌 새로 확장자를 생성한다는 등의 작업이 가능하지만 일단은 임시로 에러를
반환하도록 한다.)

정상 동작한다!


리뷰

일단은 커스텀 확장자 부분은 놔두고 고정 확장자에 대한 Spring boot 코드를
Django 를 이용해 구현하는 것을 수행해보았다!

Spring 의 Controller/Service 와 DTO, Entity 등이 어떻게 Django 에서는 이루어지는지
알 수 있는 재밌는 경험이었다 🫠
(물론 Spring 에서 추상 클래스라던지 인터페이스 부분은 어떻게 할지 결정하지 못했지만…)

결론은 쓰는 프레임워크가 다를 뿐 API 요청을 처리하기 위한 고민은 매한가지라는 것이다.
url 을 어떤 app 으로 나누어 경로를 매핑할지

Model, Template, View 로 구분하고 Json 데이터 처리를 위한 객체로 만들고 (Serializer)
API 응답을 위한 비즈니스 로직은 어떻게 구현해하는지 (DB 조회 시, is_vaild 문제, try-catch)

결국 백엔드가 해야할 일을 온전히 하기 위해 고민하는 것들은 어떤 프레임워크든 비슷한 것 같다!!

그러니까 많이 경험하면서 프레임워크에 종속되지 않는 느슨한 결합을 가진
개발자가 되어야 할 것 같다! 😎

p.s View 에서 비즈니스 로직을 분리하는 것과 추상 클래스라던지 인터페이스도 한번 수정해보자

profile
뒘벼

0개의 댓글