django에서 business login 다루는 방법 4가지

런던행·2021년 3월 2일
0

Django 업그레이드

목록 보기
7/17

장고를 처음 접하면서 비지니스 로직을 어디에 관리를 두어야할 지 막연하게 가이드가 없어 찾아보고 정리를 하였다.
비지니스 로직을 어떻게 관리를 해야할지 잘 모르는 흔한 초보 개발자라면 흔히 View단(MVT 장고 패턴)에 모든 구현 코드를 적어 실무에서 방대한 코드를 작성하고 있다. 나도 역시 그랬었다. View단에 모든 비지니스 로직을 작성 해야 할까??
4가지 가능한 방법을 알아보자.

  1. Views/Forms/Serializers
  2. Fat Models
  3. QuerySets/Manager
  4. Services

1. Views/Forms/Serializers

(1) View(MVT패턴)

비지니스 코드를 쉽게 구현하는 위치는 대 다수가 아마도 View(MVT패턴)에 작성을 하고 있지 않나 생각이 된다. 처음에 나도 역시 그랬었다.

간단한 기능과 중복사용이 없는 로직에서는 View에 작성을 하는게 좋다고 생각이 된다. 다만 기능이 비대해지고 중복 사용이 많은 상황일 때 모든 로직을 View에 넣는다면 코드 가독성이라던지 유지보수에 많은 노력이 필요 할 것이다.

(2) Forms/Serializers

Form/Serializer에도 로직을 구현할수 있다. Form/Serializer 역할에 중점을 두고 생각을 하면 데이터 유효성을 주로 다루는 역할인데 비지니스 로직을 구현하는게 과연 좋은 방향인지 모르겠다.

2. Fat Models

장고 도큐먼트에서 권장하는 방향은 Model에 비지니스 로직을 구현하는 것이다.

이 접근법의 이익은 3가지가 있다

  • 중복코드 재 사용 : view나 form에서 반복되는 코드들을 모델에 정의하여 중복 코드를 재 사용 할 수 있다.
  • 테스트 하기 쉽다 : 작은 메소드을 모델에 쪼개어서 구현하면 단위 테스트에 용이하다.
  • 가독성

아래는 예시이다. Post 모델이 있다.

class Post(models.Model):
    title = models.CharField(verbose_name='Title', max_length=50)

    STATUS_DRAFT, STATUS_APPROVED, STATUS_PUBLISHED = range(3)
       STATUS_CHOICES = (
           (STATUS_DRAFT, _('Draft')),
           (STATUS_APPROVED, _('Approved')),
           (STATUS_PUBLISHED, _('Published'))
       )

    status = models.PositiveSmallIntegerField(
           choices=STATUS_CHOICES,
           default=STATUS_DRAFT
       )
       
    class Meta:
        verbose_name = 'post'  # 테이블 별칭
        verbose_name_plural = 'posts'  # 테이블 복수 별칭
        db_table = 'blog_posts'

    def __str__(self):
        return self.title

Post모델에 status컬럼을 주목하자. 비지니스 로직 2가지 기능이 필요하다고 가정하자

  • 슈퍼 관리자에 의해서 해당 Post가 승인으로 변경해야 한다.
  • 해당 Post가 출판(publish)으로 변경해야 한다. 단 승인된 Post이어야 한다.
class Post(Model):

    ...

    def approve(self):
        self.status = Post.STATUS_APPROVED
        self.save()

    def publish(self):
        if self.status != Post.STATUS_APPROVED:
            raise PostInvalidStatusError()
        self.status = Post.STATUS_PUBLISHED
        self.save()

이 코드를 작성하지 않았던 어떤 개발자가 이 코드를 보면 분명하게 코드의 의도를 파악할 수 있다.
그러나 장고앱에서 모델에 많은 기능들이 구현 된다면 점차 아주 큰 모델을 작성하게 된다. 아주 큰 코드는 유지보수에 어려움이 생긴다. proxy 모델을 구성할 때는 필요치 않는 멤버 함수들을 상속받을 수 있다.

3. QuerySets/Manager

쿼리셋과 매니저에 비지니스로직을 위치시킬 수 있다.

class PostQuerySet(QuerySet):
   def approve(self):
       return self.update(status=Post.STATUS_APPROVED)

   def publish(self):
       return self.filter(status=Post.STATUS_APPROVED).update(
           status=Post.STATUS_PUBLISHED)


class Post(Model):
   ...

   objects = PostQuerySet.as_manager()
   
   ...

쿼리셋/매니저에 비지니스 로직을 정의했을 때 사용법은

Post.objects.get(id=1).approve()

모델에 비지니스로직을 정의했을 때 사용법은

post: Post = Post.objects.get(id=1)
post.approve()

별반 차이 없어 보이지만 쿼리셋/매니저에 비지니스 로직을 구현 시 장점은 한번의 update 쿼리로 여러개의 객체를 상태를 변경할 수 있다. 경우 즉, Batch 에서 기능을 재사용하기 편하다.

Post.objects.get(id=1).approve()
 VS
posts = Post.objects.all()
for post in posts:
	post.approve()

단점은 비지니스 로직을 찾는것이 다른 방법보다 더 어렵다.

4. Services

장고에서 서비스레이어 관련된 어떤 가이드가 없다.
모델 - 뷰 - 템플릿 MVT 패턴에서 모델-뷰 사이에 비지니스 로직을 담당하는 서비스 층을 추가하는 개념이다.

모델 - 서비스레이어 - 뷰 - 템플릿

모델에는 속성만 다룬다.

다음 포스트에서 서비스레이어를 도입하는 튜토리얼 과정을 설명하고자 한다.

레퍼런스 : https://sunscrapers.com/blog/where-to-put-business-logic-django/

profile
unit test, tdd, bdd, laravel, django, android native, vuejs, react, embedded linux, typescript

0개의 댓글