본문 바로가기
Project Trouble Shooting/[축팅] 축제 소개팅 어플리케이션 - 카카오 테크 캠퍼스 1기

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

by Big Sun 2023. 10. 13.
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