Service의 의존성 분리하기

Nine-JH·2023년 10월 11일
0

Service 에서 다른 도메인의 Repository 사용을 자제합시다!

아래의 기능은 제외했습니다.

이거 조삼모사 아닌가요?

그런데 요청받은 Service API의 Return Type이 JPA Entity 에 의존적인 클래스였습니다. 결국 DB 접근 기술에 의존적이고 싶지 않아 Service Layer로 접근하자고 하면서 리턴타입은 JPA에 의존적인 Entity? 이거 조삼모사가 아닌가 싶어 의견을 드렸습니다.

그래서 대안으로 필수 정보만 Dto로 담아서 API Spec으로 생성하자라는 말씀을 드렸지만, 다른 우선순위가 많았기 때문에 일단 Entity로 리턴하고 나중에 수정하자라는 답변을 받게 되었습니다. 저도 우선순위가 높은 부분들이 많다라는건 인정하는 부분이었기때문에 일단 진행하게 되었습니다.



문제 발생..!

일단 Entity만 바로 반환을 하면 되니 개발속도는 정말 빠릅니다. 하지만 문제는 리팩토링 단계에서 발생하게 되었는데요..!

1. 요구사항 변경

프론트엔드분들과 협업을 진행하는 와중에도 요구사항은 끊임없이 변경되었습니다. 저희 백엔드팀이 미처 캐치하지 못한 점들을 많이 짚어주셔서 정말 도움이 많이되었는데요. 문제는 여기서 발생합니다.

요구사항이 변경되면서 Table을 변경해야하는데.. Entity가 영향을 받고.. 또 이를 제공하는 Service도 영향을 받고... 또 이 API를 사용하는 다른 API도 영향을 받고...

정말 꼬리에 꼬리를 무는 리팩토링의 연속이었습니다. Entity에 필드 하나를 수정하는데에도 10개가 넘어가는 클래스가 영향을 받는 일도 비일비재했죠.



2. 불필요한 정보들

Entity를 통째로 넘기다 보니 불필요한 정보들 역시 많이 넘어가게 되었습니다. 메서드들도 마찬가지고요!
굳이 알지 않아도 되는 정보를 넘기다보니 잘못된 사용이나 수정에 대한 위험성이 존재하게 됩니다.
가령 위의 Comment 처럼 Post의 전체 정보를 넘기게 된다면, 게시글의 정보가 노출되는 위험성이 존재하게 되는 것이죠!


리팩토링 시작

위의 불편한점들을 그대로 안고 갈 수는 없죠! 수정을 해봅시다.

Entity를 반환하는 API 수정

public Post findById(Long id) {
    return postRepository.findById(id)
        .orElseThrow(PostNotFoundException::new);
}

해당 Service API를 아래와 같이 Dto로 변경합시다.

@Override
public PostDetailResponseDto findById(Long id) {
    return PostDetailResponseDto.entityToDto(postRepository.findById(id)
         .orElseThrow(PostNotFoundException::new));
}


Comment에 영향 발생!

public PostCommentResponseDTO createComment(CreateCommentRequest req) {
    ...
    
    return commentRepository.save(
            Comment.builder().post(postQueryService.findById(req.getPostId())) // ERROR!
                .member(memberService.getMember(req.getMemberId())).content(req.getContent())
                .anonymity(req.getAnonymity()).parentComment(parentComment).build())
        .toPostCommentResponseDTO();
}

바로 문제가 발생하게 됩니다. 여기서 문제는 Comment.Builder 에 더이상 post를 채워넣을 수 없다는 것이 되겠죠.

public PostCommentResponseDTO createComment(CreateCommentRequest req) {
    ...
    
    if (!postQueryService.isExist(req.getPostId())) {
        throw new PostNotFoundException(); 
    }
        
    return commentRepository.save(
            Comment.builder().post(Post.builder().id(req.getPostId()).build()) // id만 존재하는 Post로 변경!
                .member(memberService.getMember(req.getMemberId())).content(req.getContent())
                .anonymity(req.getAnonymity()).parentComment(parentComment).build())
        .toPostCommentResponseDTO();
}
  • 사실 Post의 전체 정보가 필요한게 아닙니다. 의존관계를 위해서는 게시글의 식별자인 id만 있으면 되지 않을까요?
  • 추가적으로 게시글이 존재하다는 로직만 추가하면 좋을 것 같네요!
  • 만약 Comment 내부에서 Post를 조작하는 메서드가 있다면, 사이드이펙트를 주의해야 합니다.

0개의 댓글