Service Layer를 사용하여서 표현 하였다.
가령 이런 상황이 있을 것이다.
Serializer를 이용하여서 하나의 파일을 저장을 하거나 수정을 한다.
그리고 그 파일이 성공적으로 저장, 수정이 된다면 추가로 그에 대한 로그를 저장하는 상황 말이다.
그렇다면 여기서 어떠한 문제점이 숨어있을 수 있을까?
- 예외사항
- 첫번째 파일이 성공적으로 저장이 되었는데, 두번째로 저장하는 로그에서 에러가 발생할 경우
- 이렇게 된다면 첫번째로 실행되는 로직만이 저장이 되고, 두번째 로직은 저장이 되지 않는다.
- 벌써부터 에러상황이 생기는 것이다.
- 그렇다면 어떻게 해결할 수 있을까?
Serializer를 이용하여 저장 및 업데이트를 할 때에는 is_valid()함수를 꼭 거쳐야한다.
그런데 여기서 is_valid()함수 안에 raise_execpt = True를 넣어주게되면 발생한 에러에 대해 어떠한 에러인지를 내뱉어준다.
그리고 Save()가 되기 전에 검증만을 해주는 것이다!
무슨 말인가?
기존의 방식
- 1번함수 검증
- 1번함수 저장
- 2번함수 검증
- 2번함수 저장
이러한 로직으로 인하여 1번함수가 성공적으로 저장이 되었을지라도 2번함수를 검증을 하는 단계에서 Error가 발생하여 저장이 안될 수 있다는 것이다.
그렇다면 이렇게 생각해보자
순서를 바꾼다.
- 1번함수 검증
- 2번함수 검증
- 1번함수 저장
- 2번함수 저장
이렇게 로직을 짠다면, 모든 검증이 끝난 후 저장이 이루어지기에 하나라도 에러가 발생한다면 둘 다 저장이 되지 않는다.
만약 둘 다 정상적으로 에러없이 통과했다면, 둘 다 저장이 되도록 할 수 있다.
이것을 로직으로 살펴보도록 하자.
def update_post(user : int, post_id : int, update_post_data : Dict[str, str])-> Dict[str, Union[PostSerializer, PostUpdateLogSerializer]]:
"""
모든게시판의 Update를 담당하는 Service
update_post에 대한 검증이 이루어지면 update_log를 생성하는 함수에 대한 검증 실행
두개 모두의 검증이 끝나야 저장
Args :
user (UserModel): user.User 외래키 (request.user를 통해 로그인한 유저 반환),
post_id (int): posts.Post 외래키, url에 담아서 보내줌,
update_post_data (dict): {
"title" (str): 게시글의 제목 or
"content" (str) : 게시글의 내용
}
Return :
dict[str, Union[PostSerializer, PostUpdateLogSerializer]]
"""
log_data = {"user" : user.id, "post" : post_id}
update_post = PostModel.objects.get(id=post_id)
update_post_serializer = PostSerializer(update_post, update_post_data, partial=True)
update_post_serializer.is_valid(raise_exception=True)
post_update_log_serializer = PostUpdateLogSerializer(data=log_data)
post_update_log_serializer.is_valid(raise_exception=True)
update_post_serializer.save()
post_update_log_serializer.save()
return (
{"update_post" : update_post_serializer.data},
{"update_log" : post_update_log_serializer.data}
)
update_post_serializer에 대한 검증,
post_update_log_serializer 에 대한 검증
이렇게 두개의 서비스 로직을 합침으로서 Error를 핸들링 할 수 있게 되었다.