DRF - Service Layer 추가하기

Dongwoo Kim·2022년 9월 5일
0

DRF

목록 보기
1/9
post-custom-banner

1. Service Layer 란?

API 요청이 들어왔을 때 Views에서 바로 처리하는 것이 아니라 Service Layer를 로직에 추가하여 실제 기능과 API 응답처리를 나눠 관리하는 것


2. 목적

Service Layer를 추가함으로써 Veiws의 크기를 줄일 수 있고 기능별로 예외처리, 테스트코드 작성에 용이하다.


3. 방법

구현하고자 하는 앱에 service 폴더를 만들고 구현하고자하는 기능을 작성한다.

예시) user 앱에 Service라는 폴더 생성 후 user_service.py에 기능 구현


4. 예시) 유저 회원가입 기능

1) 기존 코드 구조

회원가입 기능 + 결과 핸들링 코드
# user.views.py
# 유저 CRUD 기능
class UserView(APIView):
	...
  # 회원 가입
  def post(self, request):
      user_serializer = UserSerializer(data=request.data)
      if user_serializer.is_valid():  # validation
          user_serializer.save()      # create
          return Response({'message': '저장 완료!'}, status=status.HTTP_200_OK)

      return Response(user_serializer.errors, status=status.HTTP_400_BAD_REQUEST)

2) Service Layer 추가한 구조

회원가입 기능 구현 코드
# user.service.user_service.py
def user_post_service(user_info: dict):
  """ 
      사용자정보로 회원가입하는 함수

  Args:
      user_info (dict): 회원가입할 유저 정보 


  Raises:
      Todo) 예외처리 작업중

  """

  user_type_str = user_info.pop("user_type", "general")

  user_type_obj = UserType.objects.get(user_type=user_type_str)
  user_serializer = UserSerializer(data=user_info)
  user_serializer.is_valid(raise_exception=True)
  user_serializer.save(user_type=user_type_obj)

회원가입 결과 핸들링 코드
# user.views.py
from user.Service.user_service import (
  user_post_service,
  )
...

# 유저 CRUD 기능
class UserView(APIView):
	...
  # 회원가입 기능
  def post(self, request):
      try:
          user_post_service(request.data)
          return Response({"detail": "회원가입 성공"}, status=status.HTTP_200_OK)
      except AssertionError:
          return Response({"detail": "회원가입에 실패했습니다."}, status=status.HTTP_400_BAD_REQUEST)

3) 개선 사항

3-1) 테스트 코드 작성

회원가입 기능과 회원가입 결과에 따른 예외처리 기능을 나눠서 처리 할 수 있다.

회원가입 기능 테스트 - 정상 작동
  # user.tests.service.test_user_service.py
  # 회원가입 기능 테스트

  def test_user_create_service(self):
      """ 
      회원가입 기능 테스트
      
      case : 정상 작동
      """

      user_info = {
          "username": "test", 
          "password": "test_password",
          "gender": "female",
          "age": "45",
          "phone": "010-1212-1212",
          "user_type": "general" 
      }
          
      count_1 = User.objects.all().count()

      with self.assertNumQueries(5):
          user_post_service(user_info)
      
      count_2 = User.objects.all().count()
      
      self.assertEqual(count_1 + 1, count_2)

회원가입 기능 테스트 - 유저 오브젝트가 제대로 생성되지 않았을 경우
 # user.tests.service.test_user_service.py

 def test_user_create_when_does_not_create_object(self):
      """ 
      회원가입 기능 테스트
      
      case : 유저 오브젝트가 제대로 생성되지 않았을 경우
      """

      user_info = {
          "username": "test", 
          "password": "test_password",
          "gender": "female",
          "age": "45",
          "phone": "010-1212-1212",
          "user_type": "general" 
      }

      count_1 = User.objects.all().count()

      with self.assertRaises(AssertionError):
          with self.assertNumQueries(4):
              user_post_service(user_info)
      
      count_2 = User.objects.all().count()
      
      with self.assertRaises(AssertionError):
          self.assertNotEqual(count_1 + 1, count_2)



참고)

작성 코드 : https://github.com/PreOnBoarding/Bulletin_Board_Service

profile
kimphysicsman
post-custom-banner

0개의 댓글