ComposeMethod을 적용해 리팩터링 해보자

2023. 10. 13. 11:47프로젝트/[축팅]축제 소개팅 어플리케이션 - 카카오 테크 캠퍼스 1기

728x90



현재 카카오 테크 캠퍼스 3단계에서 축팅이라는 서비스의 인기 피드 부분를 담당해 개발하고 있는 중입니다.

개발 하는 중 멘토님께서 피드백해주신 내용을 블로그로 포스팅해보려고 합니다~

 

 

현재 코드

 

  @Transactional
  public void execute(){
    List<GetIncompletePopularPostDTO> top300Posts = postRepository.findTop300ByOrderByPopularityDesc(PageRequest.of(0, POPULARITY_SIZE));
    redisTemplate.delete(RedisKey.POPULAR_POST_KEY.getKey());
    top300Posts.forEach(getIncompletePopularPostDTO -> {
      redisTemplate.opsForZSet().add(RedisKey.POPULAR_POST_KEY.getKey(), getIncompletePopularPostDTO, getIncompletePopularPostDTO.getPopularity().doubleValue());
    });
  }



위는 MySQL에서 인기도를 기반으로 상위 300개의 게시물을 가져와 이를 Redis의 SortedSet 자료구조에 저장하고 있는 코드이다.

위 코드에서 아래와 같은 피드백을 받았다.


 

이 메서드는 좀 장황해서 읽기 어렵습니다. 무슨 일을 하는지 다 읽어야 알 수 있습니다.

코드는 신문이나 책처럼 읽혀야 좋습니다.

비유하면 퍼블릭 메서드의 이름은 신문 기사의 제목입니다.
퍼블릭 메서드 안에서 호출하는 메서드들은 부제목이고요.
나머지 메서드들은 더 디테일하고 추상화 수준이 낮습니다.

추상화 레벨이 고수준 -> 중간수준 -> 저수준으로 이어져야 코드를 읽기 쉽습니다.

어려운 말로하면 ComposeMethod을 도입해서 SingleLayerOfAbstraction 을 이룰 수 있습니다. :)




멘토님께서 리팩터링 해주신 코드는 아래와 같다.

 

  @Transactional
  public void execute(){
    List<GetIncompletePopularPostDTO> top300Posts = getTop300Posts();
    deletePopularPostsCache();
    setPopularPostsCache(top300Posts);
  }

  private List<GetIncompletePopularPostDTO> getTop300Posts(){
    return postRepository.findTop300ByOrderByPopularityDesc(PageRequest.of(0, POPULARITY_SIZE));
  }

  private void deletePopularPostsCache(){
    redisTemplate.delete(RedisKey.POPULAR_POST_KEY.getKey());
  }

  private void setPopularPostsCache(List<GetIncompletePopularPostDTO> top300Posts){
    top300Posts.forEach(this::setPopularPostsCache);
  }

  private void setPopularPostsCache(GetIncompletePopularPostDTO dto) {
    ZSetOperations zSetOperations = redisTemplate.opsForZSet();
    double score = dto.getPopularity().doubleValue();
    zSetOperations.add(RedisKey.POPULAR_POST_KEY.getKey(), dto, score);
  }

 


왜 이렇게 리팩터링을 했는 지에대해서 이해하려면, Composed Method를 이해해야한다.



Composed Method



Composed Method는 Ket Bech의 "Smalltalk Best Practice Patterns"에 나오는 기법이다.

 

이는 메서드를 단순하게 유지하는 것이며, 각 메서드를 가능한 작고 하나의 작업만 수행하도록 만드는 것이라고 한다.



규칙은 아래와 같습니다.



  1. 메서드가 수행하는 모든 작업이 같은 추상화 수준에 있어야 한다.(Keep all of the operations in a method at the same level of abstraction)
  2. 메서드가 한 번에 하나의 작업만 수행해야 한다.(Divide your program into methods that perform one identifiable task)
  3. 한 메서드에서 다른 메서드를 호출하면, 그 메서드는 완전한 작업을 수행해야 한다.(This will naturally result in programs with many small methods, each a few lines long)



1번의 규칙을 바탕으로 리팩터링한 코드를 이해해보면, execute() 메서드를 추상화 레벨이 고수준이라 한다면, execute() 메서드에서 호출하는 메서드들

즉, getTop300Posts(), deletePopularPostsCache(), setPopularPostsCache(List<GetIncompletePopularPostDTO> top300Posts) 들을 추상화 레벨은 중수준이라고 할 수 있겠다.

그리고, setPopularPostsCacheI(GetIncompletePopularPostDTO dto) 메서드는 setPopularPostsCache(List top300Posts)에서 호출하므로 이 메서드의 추상화 레벨은 저수준이다.



 private void setPopularPostsCache(List<GetIncompletePopularPostDTO> top300Posts){
    top300Posts.forEach(this::setPopularPostsCache);
  }

 private void setPopularPostsCache(GetIncompletePopularPostDTO dto) {
    ZSetOperations zSetOperations = redisTemplate.opsForZSet();
    double score = dto.getPopularity().doubleValue();
    zSetOperations.add(RedisKey.POPULAR_POST_KEY.getKey(), dto, score);
  }

 

멘토님께서 리팩터링 해주신 코드에서 Composed Method에 대해 이해하기 전까지 이 부분이 완벽히 이해가 되지 않았다.

 

이해가 잘 되지 않는 부분은 아래와 같다.

 



왜 아래와 같이 하지 않고 2개의 메서드로 나눴을까?

 

 private void setPopularPostsCache(List<GetIncompletePopularPostDTO> top300Posts){
    top300Posts.forEach(getIncompletePopularPostDTO -> {
      redisTemplate.opsForZSet().add(RedisKey.POPULAR_POST_KEY.getKey(), getIncompletePopularPostDTO, getIncompletePopularPostDTO.getPopularity().doubleValue());
    });
 }

 

이는 Composed Method 규칙 2와 3과 관련이 있는 데, forEach 즉 top300Posts들을 하나하나 나누는 작업과 Redis에 저장하는 작업을 나눈 것이다. 

 

 

멘토님의 피드백을 통해, 코드의 모듈화와 메서드 분리를 통해  코드의 가독성을 높이고 유지보수를 용이하게 해주는 ComposeMethod에 대해 알고 이를 고민해보는 시간을 가질 수 있었다.

 

멘토님 감사합니다.

 

 



아래는 해당 PR 링크 입니다.

https://github.com/Step3-kakao-tech-campus/Team14_BE/pull/86

 

인기 피드 리팩토링 및 테스트 보충 by hwangdaesun · Pull Request #86 · Step3-kakao-tech-campus/Team14_BE

인기 피드 상세 조회 및 전체조회 관련 코드들을 리팩토링하였습니다. Commit 요약 refactor : RedisKey 관련 명명 수정 refactor : 주석 추가 test : 인기있는 상위 300개 게시물 조회하는 SQL 테스트 및 테스

github.com

 

 

 

728x90