티스토리 뷰

Project

[Refactoring] 전략 패턴 적용하기 (1)

Nickolodeon 2023. 2. 22. 07:26
Introduction
프로젝트를 하던 중, DB 에서 테이블을 조회하는 코드가 중복이 많다는 것을 알게 되었다. 이후 중복을 없애기 위해 디자인 패턴을 적용하자고 마음을 먹고 전략 패턴을 적용해보았다.

패턴 적용 전 코드

  public SearchPageGetResponse<TrackGetResponse> findTracksWithKeyword(Pageable pageable, String searchKey, String memberEmail) {
    Member member = validateMember(memberEmail);
    
    Page<TrackGetResponse> tracks = trackRepository
    	.findAllByTrackTitleContainingOrAlbumAlbumTitleContainingOrArtistArtistNameContaining(keyword, keyword, keyword, pageable)
    	.map(trackPage -> trackPage.map(TrackGetResponse::of))
    	.orElseGet(() -> new PageImpl<>(Collections.emptyList()));
    
    return tracks.isEmpty() ?
        SearchPageGetResponse.of(message.substring(0,message.length()-1) + " 검색 결과가 없습니다.", pagedItems)
        : SearchPageGetResponse.of(
            String.format("'%s' 으로 총 %s개의 음원을 찾았습니다.",
                searchKey,
                tracks.getTotalElements()), tracks);
  }
  
  public SearchPageGetResponse<SearchMembersResponse> findMembersWithKeyword(Pageable pageable, String searchKey, String memberEmail) {
    Member member = validateMember(memberEmail);
    
    Page<SearchMembersResponse> members = memberRepository
    	.findByNickNameContaining(keyword, pageable)
        .map(memberPage -> memberPage.map(SearchMembersResponse::of))
        .orElseGet(() -> new PageImpl<>(Collections.emptyList()));

    return members.isEmpty() ?
        SearchPageGetResponse.of(message.substring(0,message.length()-1) + " 검색 결과가 없습니다.", pagedItems)
        : SearchPageGetResponse.of(
            String.format("'%s' 으로 총 %s개의 사용자를 찾았습니다.",
                searchKey,
                members.getTotalElements()), members);
  }
  
  public SearchPageGetResponse<SearchRecommendsResponse> findMembersWithKeyword(Pageable pageable, String searchKey, String memberEmail) {
    Member member = validateMember(memberEmail);
    
    Page<SearchRecommendsResponse> recommends =
    	recommendRepository.findByRecommendTitleContaining(keyword, pageable)
        .map(recommendPage -> recommendPage.map(SearchRecommendRespons::of))
        .orElseGet(() -> new PageImpl<>(Collections.emptyList()));
        
    return recommends.isEmpty() ?
        SearchPageGetResponse.of(message.substring(0,message.length()-1) + " 검색 결과가 없습니다.", pagedItems)
        : SearchPageGetResponse.of(
            String.format("'%s' 으로 총 %s개의 추천글을 찾았습니다.",
                searchKey,
                recommends.getTotalElements()), recommends);
  }

중복이 많다는 말을 간단하게 설명할 수 있는 코드를 가져왔다. 위는 패턴 적용 전 전형적인 방법으로 레포지토리 (EntityManager) 를 활용해서 DI 를 받은 repository 의 메서드들을 목적에 맞게 메소드에서 사용하고 반환한 모습이다. 코드의 중복이 매우 많은 것을 볼 수 있다.

 

전략 패턴 적용 후 코드

중복을 하나의 메서드로 분리하기

위의 코드에서 중복을 제거하려면 메서드 하나에 각 메서드에서 중복되는 부분은 작성하고, 메서드별로 달라지는 부분은 매개변수로 받는 객체의 동작을 이용해 다르게 정의하는 방식을 사용할 수 있다.

 

전략 패턴을 사용하면 위 메서드 세 개를 포괄하는 하나의 메서드를 정의할 수 있다. Java 의 제너릭 메서드 정의 방식을 활용하여 T 반환 타입의 메서드를 정의한다. T 반환 타입이란 어떤 반환 타입의 객체도 반환할 수 있다는 것을 의미한다. 

 

살펴보니, 아래와 같은 코드가 중복되고 있었다:

  • 매개변수로 입력 받은 사용자 이메일 정보를 검증하는 코드
  • 엔티티를 조회하는 코드
  • 조회 성공 시 메시지를 반환하는 코드

이 중복들을 모아 작성한 하나의 메서드는 아래와 같다.

  public <T> SearchPageGetResponse<T> findWithKeyword(Pageable pageable, String searchKey, ItemSearch<T> searchTool) {
    Member member = validate(memberEmail, new MemberValidation(memberRepository));

    Page<T> pagedItems = searchTool.search(searchKey, pageable);

    String message = searchTool.buildMessage();

    return pagedItems.isEmpty() ?
        SearchPageGetResponse.of(message.substring(0,message.length()-1) + " 검색 결과가 없습니다.", pagedItems)
        : SearchPageGetResponse.of(
            String.format("'%s' 으로 총 %s개의 %s 찾았습니다.",
                searchKey,
                pagedItems.getTotalElements(), message), pagedItems);
  }

이 코드에 대한 설명 부터 다음 포스트에서 이어서 하도록 하겠다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함