[엉박사] 1.8 services.py

impala·2023년 1월 12일
0
post-thumbnail

1.8 services.py

장고에서 비즈니스 로직을 넣는 방법은 여러가지가 있는데 자주 쓰이는 방법은 4가지가 있다.

  • View, Form, Serializer
    • 일반적인 경우로 views.py, forms.py, serializers.py에 비즈니스 로직을 작성한다.
    • 로직이 커질수록 각 파일의 역할을 직관적으로 파악하기 어려워진다.
  • Fat Models
    • models.py에 비즈니스 로직을 작성하는 방법
    • 위의 방법과 마찬가지로 로직이 커질수록 유지보수가 어렵다
  • Service Layer
    • 비즈니스 로직을 services.py라는 별도의 파일을 만들어 작성한다.
    • 비즈니스 로직이 분리되어있어 코드가 깔끔하고 유지보수가 쉬워지지만 장고 기본패턴에 부합하지 않기 때문에 구현이 복잡할 수 있다.
    • 비즈니스 로직에 대한 Test code작성이 쉽다.
  • Queryset / Manager
    • models.py에 QuerySet클래스를 만들어 비즈니스 로직을 작성한다.
    • Django ORM을 통해 비즈니스 로직을 처리하는 방법
    • 장고에서 queryset을 많이 사용하기 때문에 사용이 편리하고, 여러 데이터에 대한 처리를 한번의 쿼리로 처리할 수 있지만 비즈니스 모델을 구현하기 까다로운 경우가 있다.

이번 프로젝트에서는 서비스에 자연어처리 모델이 포함되기 때문에 API에 대한 코드와 AI모델에 대한 코드가 같은 공간에 있으면 가독성도 떨어지고 관리가 어려울 것 같아 로직을 둘로 분리하였다.

즉, 자연어 처리에 대한 로직은 services.py라는 별도의 파일을 만들어 관리하고, 서비스 자체에 대한 로직은 serialisers.py에서 담당하도록 하여 유지보수의 편의성을 높였다.

# /report/serializers.py
 def to_internal_value(self, data):
        ...

        # services.py에 분리되어있는 자연어 처리 로직 호출
        correct = ReportConfig.model.spellCheck(text.original)
        text.correct = correct["correct"]
        feedback = ReportConfig.model.getFeedBack(text.correct)
        text.feedback = feedback["feedback"]
        text.save()

        # 피드백 결과를 바탕으로 사용자의 점수를 갱신하는 로직
        user = data["user"]
        score = [
            int(text.report.quiz_score / (text.report.book.chapters * 5) * 100),    
            correct["score"],                                                       
            int((sum(feedback["score"][:3]) / (3 * MAX_SCORE)) * 100),              
            int((sum(feedback["score"][3:7]) / (4 * MAX_SCORE)) * 100),            
            int((sum(feedback["score"][7:]) / (4 * MAX_SCORE)) * 100),              
        ]
        user.updateScore(score)

        ...

# /report/services.py
class Feedback():
    ...

    def getScore(self, text):
        # 감상문을 받아 모델에 적용시켜 점수를 추출하는 로직

        essays = self.preprocessing(text)
        inputs = self.tokenizer.batch_encode_plus(essays, is_split_into_words=True)
        ids_new = pad_sequences(inputs['input_ids'],
                                maxlen=self.MAXLEN, padding='post')
        mask_new = pad_sequences(inputs['attention_mask'], 
                                maxlen=self.MAXLEN, padding='post')
        out = self.embeding_model(input_ids=torch.tensor(ids_new).to(self.device),
                                attention_mask=torch.tensor(mask_new).to(self.device))
        
        ...
        return model_outputs.tolist()[0]

    def getFeedBack(self, text, age=11):
        # 채점된 점수를 바탕으로 피드백을 만드는 로직

        result = {"feedback": "", "score": []}
        result["score"] = self.getScore(text)
        scores = list(map(lambda x: math.floor(3 * x), result["score"]))

       ...
        if not feedback_bad:
            result["feedback"] = ' 또한 '.join(feedback_good[:2])
        elif not feedback_good:
            result["feedback"] = ' 그리고 '.join(feedback_bad[:2])
        else:
            result["feedback"] = feedback_good[0] + ' 하지만 ' + feedback_bad[0]

        return result

0개의 댓글