장고에서 비즈니스 로직을 넣는 방법은 여러가지가 있는데 자주 쓰이는 방법은 4가지가 있다.
이번 프로젝트에서는 서비스에 자연어처리 모델이 포함되기 때문에 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