프로젝트를 진행하면서 하나의 Service에서 너무많은 책임(기능)을 가져서
하나의 함수가 40줄이여서 가독성도 불편했고
무엇보다 Repository도 많이 가지고 있어서 너무많은 책임을 가지고 있었습니다.
그래서 이거를 어떻게 해결할까 고민을 하다가 2가지 방법을 고민해봤습니다.
둘다 단점이 있어서 어떻게 해야할지 고민을 좀 하다가
Controller 단에서 Service를 조합하여 최종적인 Response를 내보내면 좋을 것같다고 생각했습니다.
// PostController 예시
@GetMapping("/post/{id}")
public ResponseFormat<Post> getPost(@PathVariable Long id) {
Post post = postService.findPost(id, getUser());
return new ResponseFormat(post);
}
// Post Service 예시
@Service
@RequiredArgsConstructor
public class PostService {
private final PostRepository postRepository;
private final VoteRepository voteRepository;
private final CategoryInPostRepository categoryInPostRepository;
private final VoteSelectRepository voteSelectRepository;
private final PostLikeRepository postLikeRepository;
@Transactional
public PostDto findPost(Long id, UserEntity userEntity) {
// 1. post 정보 얻기
Post 정보 얻어오는 코드들
...
// 2. vote 정보 얻기
Vote 정보 얻어오는 코드들
...
// 3. 해당 Post가 어떤 category를 가지고 있는지 정보 얻기
Category 정보 얻어오는 코드들
...
// 4. 현재 user가 어떤 투표를 했는지 얻기
현재 User가 투표한 결과 얻어오는 코드들
...
// 5. 현재 user가 좋아요 했는지 얻기
User가 좋아요 했는지 얻어오는 코드들
...
// 6. DTO 로 변환하는 코드들
...
return postDto;
}
}
이런식으로 PostService에서 특정 Post를 얻는 함수가 43줄이라는 함수가 완성되었습니다!!
코드를 보시면 총 6가지 기능을 하는 것을 볼수가 있었습니다.
게다가 하나의 Service가 가지고 있는 Repository들을 보면.......
하나의 함수가 6가지 기능을 수행한다? 너무나 끔찍한 코드를 저는 완성시킨거였습니다 ㅠㅠ
그래서 저는 저 기능들을 여러 Service들로 구성하여 Controller 계층에서 조립을 하였습니다.
// PostController
@GetMapping("/post/{id}")
public ResponseFormat<Post> getPost(@PathVariable Long id) {
UserEntity userEntity = getUser();
PostEntity postEntity = postService.findPost(id);
List<Vote> votes = voteService.findVotes(postEntity);
List<Category> categories = categoryInPostService.findCagegoriesInPost(postEntity);
Integer voteResult = voteSelectService.findVoteSelectResult(userEntity, postEntity);
boolean isLike = postLikeService.findPostIsLike(userEntity, postEntity);
// DTO 변환 하는 코드들
...
return new ResponseFormat(post);
}
// PostService
private final PostRepository postRepository;
@Transactional
public PostEntity findPost(Long id) {
// post 정보 얻기 (단 3줄)
return postEntity;
}
// VoteService
@Transactional
public List<Vote> findVotes(PostEntity postEntity) {
// vote 정보 얻기 (단 4줄)
return votes;
}
// CategoryService
@Transactional
public List<Category> findCagegoriesInPost(PostEntity postEntity) {
// 해당 Post가 어떤 category를 가지고 있는지 정보 얻기 (단 5줄)
return categories;
}
// VoteSelectService
@Transactional
public Integer findVoteSelectResult(UserEntity userEntity, PostEntity postEntity) {
// 현재 user가 어떤 투표를 했는지 얻기 (단 4줄)
return voteResult;
}
// PostLikeService
@Transactional
public boolean findPostIsLike(UserEntity userEntity, PostEntity postEntity) {
// 현재 user가 좋아요 했는지 얻기 (단 4줄)
return isLike;
}
자 이런식으로 책임을 분할하여 여러 Service를 만들었고 Controller에서 Service들의 결과를 조립하여 Response를 만들어 보냈습니다.
이렇게 해서 얻었던 이점들은
Controller -> Service -> Repository -> DB
이러한 기존 계층 구조를 유지하면서 서비스의 책임을 줄여가지고
비즈니스 로직을 짜기 수월하게 됐는데
제가 생각했을 때는 이게 최선의 방법 같습니다.
스스로 특정 Service가 책임이 많다고 느끼고 관심사의 분리의 필요성을 느껴
제대로 리팩토링 한게 처음인거같은데
각성한것처럼 뿌듯하네요 ㅋㅋㅋㅋㅋㅋㅋㅋㅋ
그리고 Controller -> Service -> Repository -> DB
계층 구조이기 때문에 관심사의 분리를 좀더 수월하게 할 수있지 않았나 생각됩니다.
그래서 다양한 디자인패턴과 여러 패턴들이 관심사의 분리를 위해서 나온
개발자들의 많은 고민과 노력이 들어간 패턴들이라고 생각합니다.
다른 코드들을 참고하다가
하나의 Service 내에서 기능별 함수들을 만들어서
함수를 조합해서 쓰는 코드를 봤었는데
기능별로 함수를 구현해서 함수 자체는 간결해졌지만
이 역시 하나의 Service가 여러 책임(기능)들을 가지고 있기 때문에
하나의 Service가 무겁지 않나 라는 생각이 들어서 그렇게 좋은 코드는 아니라고 생각합니다.
너무 잘봤습니다 플젝에 반영해봐야겠네요!! 감사합니다^^