스프링 공부/게시판 프로젝트 만들기

[스프링] 29. MemberRepository.search를 최적화시켜보자!!

장아장 2023. 2. 9. 21:04

저번의 생각을 토대로, 조금 더 최적화를 해보려고 했다. 

일단 처음의 코드를 보자면, 

private BooleanExpression usernameEq(String username, SearchType searchType){
    if(username == null || username.isEmpty() || username.isBlank()) return null;
    if(searchType.equals(CONTAINS)) return member.username.contains(username);
    return member.username.eq(username);
}

private BooleanExpression nicknameEq(String nickname, SearchType searchType){
    if(nickname == null || nickname.isEmpty() || nickname.isBlank()) return null;
    if(searchType.equals(CONTAINS)) return member.nickname.contains(nickname);
    return member.nickname.eq(nickname);
}

private BooleanExpression emailEq(String email, SearchType searchType){
    if(email == null || email.isEmpty() || email.isBlank()) return null;
    if(searchType.equals(CONTAINS)) return member.email.contains(email);
    return member.email.eq(email);
}

이런식으로 만들어서 모든 메서드를 ','(and 연산)으로 추가했는데, 이렇게 되었을 때,

파라미터를 정확히 알지 못하는 상태에서는 아무것도 구하지 못할 것 이라는 문제가 있었다. 

 

개인적으로, 한 멤버가 다른 멤버를 검색할 때에는, 정확한 정보를 몰라서 검색하는 것이라고 생각한다. 

그러면, and연산보다는 or로 많더라도 데이터를 전부 보여주는게 좋지 않을까 라는 생각이 들었다. 

 

private BooleanBuilder searchCondition(SearchMemberDto searchMemberDto, SearchType searchType){
    BooleanBuilder builder = new BooleanBuilder();
    if(StringUtils.hasText(searchMemberDto.getUsername())){
        builder.or(queryUsername(searchMemberDto.getUsername(), searchType));
    }
    if(StringUtils.hasText(searchMemberDto.getNickname()))
        builder.or(queryNickname(searchMemberDto.getNickname(), searchType));
    if(StringUtils.hasText(searchMemberDto.getEmail()))
        builder.or(queryEmail(searchMemberDto.getEmail(), searchType));
    return builder;
}

private Predicate queryUsername(String username, SearchType searchType){
    if(searchType.equals(EXACT))
        return member.username.eq(username);
    return member.username.contains(username);
}

private Predicate queryNickname(String nickname, SearchType searchType){
    if(searchType.equals(EXACT))
        return member.nickname.eq(nickname);
    return member.nickname.contains(nickname);
}

private Predicate queryEmail(String email, SearchType searchType){
    if(searchType.equals(EXACT))
        return member.email.eq(email);
    return member.email.contains(email);
}

이렇게 기능을 만들어서, 검색할 파라미터를 가지고, searchType을 토대로 쿼리를 다르게 만드는 코드를 만들어보았다.

이렇게 했을 때 문제를 생각해보면, 

  • Username
    • 내용이 존재하는가?
      • SearchType이 어떤가?
        • SearchType에 맞는 쿼리문 반환
  • Nickname
    • 내용이 존재하는가?
      • SearchType이 어떤가?
        • SearchType에 맞는 쿼리문 반환
  • Email
    • 내용이 존재하는가?
      • SearchType이 어떤가?
        • SearchType에 맞는 쿼리문 반환

 

이게 반복되는 문제가 있다. 

 

처음에 SearchType을 한번만 체크하고, 검색값을 가지고 바로 쿼리를 만들면서, 코드를 더 깔끔하게 쓸 수 있는 방법이 무엇일까를 

조금 더 생각해보았다.

 

 

private BooleanBuilder searchCondition(SearchMemberDto searchMemberDto, SearchType searchType){
    if(searchType.equals(EXACT)) return exactSearch(searchMemberDto);
    return containsSearch(searchMemberDto);
}

private BooleanBuilder exactSearch(SearchMemberDto searchMemberDto) {
    BooleanBuilder builder = new BooleanBuilder();
    if(hasText(searchMemberDto.getUsername())) builder.or(member.username.eq(searchMemberDto.getNickname()));
    if(hasText(searchMemberDto.getNickname())) builder.or(member.nickname.eq(searchMemberDto.getNickname()));
    if(hasText(searchMemberDto.getEmail())) builder.or(member.email.eq(searchMemberDto.getEmail()));
    return builder;
}

private BooleanBuilder containsSearch(SearchMemberDto searchMemberDto){
    BooleanBuilder builder = new BooleanBuilder();
    if(hasText(searchMemberDto.getUsername())) builder.or(member.username.contains(searchMemberDto.getNickname()));
    if(hasText(searchMemberDto.getNickname())) builder.or(member.nickname.contains(searchMemberDto.getNickname()));
    if(hasText(searchMemberDto.getEmail())) builder.or(member.email.contains(searchMemberDto.getEmail()));
    return builder;
}

이렇게 되었을 때, 그 차이점을 보자면, 

  • SearchType에 따라 만드는 방식을 정한다. 
    • Username
      • 내용이 존재하는가?
        • 반환
    • Nickname
      • 내용이 존재하는가?
        • 반환
    • Email
      • 내용이 존재하는가?
        • 반환

이렇게 보면, 내용상에 큰 차이는 없어 보일 수 있지만, 

결국 하나의 결과까지 오는데, if문을 얼마나 타야할까? 를 생각해보면 이렇게 만든 이유를 알 수 있다. 

쩌어 위에 코드는 쿼리문을 만드는데 까지, if문을 6번을 타게 된다. 

하지만 방금 코드는 쿼리문을 만드는데 까지, if문을 4번을 타게 된다. 

.

..

...

그렇다.

6은 4보다 크다!!!

오늘 쓴 것 중에 가장 굉장한 정보다!!! 6은 4보다 크다!!!

이렇게 만든 후에, MemberRepositoryTest_Search_Contains를 제대로 동작시켜보았다.

이렇게 기존의 테스트는 잘 동작되는 것을 확인했다. 

이제, 정말 or로 입력한 데이터와 동일한게 모두 나오는가를 체크해봐야겠다.