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

[스프링] 27. MemberService.search의 기능을 조금 더 다양하게 만들어보기(페이징, like이용)

장아장 2023. 2. 9. 12:51
private static final PageRequest page = PageRequest.of(0, 3);

search 하나로 더욱 다양한 기능을 만들 방법을 생각해보았다. 

  • 페이징을 추가하자
    • RequestParam방식으로 페이지를 입력받아 몇 번째 페이지인지를 알려줄 것이다.  
    • 한 페이지당 기본으로 10개정도의 결과를 가지게 반환시킬 계획이다. 
      멤버 조회시 아이디, 닉네임, 이메일이 나오므로 총 30줄정도가 나올 것 이므로 10개 정도가 적당하다는 생각이 들었다. 
  • contains를 동적으로 추가하자
    • enum클래스를 만들어 RequestParam으로 검색방식을 정확한 검색인지, 혹은 검색하는 문구가 포함된 값을 구하는지를 정하게 할 것이다. 
    • 이전에 만들었던 ~~Eq메서드에 enum타입의 값을 받아 만약 Contains를 요청하는 것이라면, contains쿼리문을 실행시킬 계획이다. 

 

이렇게 기능을 추가할 계획이다. 

페이징의 경우에는 단순하게 처리가 가능하다. 

 

  1. 찾으려는 페이지를 입력받는다(페이지의 크기는 위에서 말한대로 10으로 고정할 계획이다)
  2. 이 페이지와, 10이라는 사이즈를 입력해 PageRequest를 생성해준다. 
  3. 리포지토리 기능을 동작시킬 땐, Pageable 파라미터를 추가해준 뒤, PageRequest를 넣는다. 
  4. 끄읕

그러면 이게 동작하기 위해서 Repository부터 기능을 추가할 계획이다. 

@Override
public Page<SearchMemberDto> search(SearchMemberDto searchMemberDto, Pageable pageable) {
    if(searchMemberDto.getUsername() == null && searchMemberDto.getNickname() == null && searchMemberDto.getEmail() == null)
        throw new NeedToAddSearchConditionException();
    QueryResults<SearchMemberDto> result =  query
            .select(new QSearchMemberDto(member.username, member.nickname, member.email))
            .from(member)
            .where(usernameEq(searchMemberDto.getUsername()),
                    nicknameEq(searchMemberDto.getNickname()),
                    emailEq(searchMemberDto.getEmail()))
            .offset(pageable.getOffset())
            .limit(pageable.getPageSize())
            .fetchResults();
    return new PageImpl<SearchMemberDto>(result.getResults(), pageable, result.getTotal());
}

이런식으로 Page타입으로 메서드를 변경해주어야 한다. 이는 MemberCustomRepository도 마찬가지이다. 

이후, Query를 바로 반환하는 것이 아닌, QueryResult로 일단 받아둬야 한다. 

추가되는 코드로는, offset, limit, fetchResults가 존재한다. 

  • offset : 시작되는 인덱스를 알려준다. 
  • limit : 페이지당 크기를 알려준다. 
  • fetchResults : 말 그대로 결과값을 fetch(잡아온다)

이러한 결과를 PageImpl로 반환해주는데, 이 때 파라미터를 (쿼리의 결과, Pageable, 총 개수)로 생성해 반환해주면 페이징 기능이 만들어진다!

 

이후, 검색 방식도 추가한 뒤에, 최종적인 테스트를 만들어보려고 한다. 

일단, 검색 방식이 어떻게 있을까?

  • 정확한 사람의 아이디를 가지고 검색을 할 경우
  • 정확하진 않지만, 비슷한 아이디를 찾는 경우

로 나눌 수 있다. 

 

이를 만들기 위해, 

private BooleanExpression usernameEq(String username){
    if(username == null || username.isEmpty() || username.isBlank()) return null;
    return member.username.eq(username);
}

이렇게 있던 Eq들을 수정할 계획이다. 

일단 enum타입 클래스를 만들어두었다.

public enum SearchType {

    LIKE("like"), EXACT("exact");

    private String type;

    private SearchType(String type){
        this.type = type;
    }

    public String getType(){
        return this.type;
    }
}

이렇게 enum타입을 만들어두고, 이를 활용하기로 했다. 

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);
}

만약, 검색값이 null이면 그냥 조회하고, 

SearchType이 LIKE라면, contains쿼리를 보내게 했다. 

 

다른 메서드들에도 다 추가를 해주었고, MemberCustomRepository 인터페이스에도 파라미터를 추가해주었다. 

이후, 서비스도 파라미터를 추가했다. 

@Transactional(readOnly = true)
public Page<SearchMemberDto> search(SearchMemberDto searchMemberDto, Pageable pageable, SearchType searchType){
    return memberRepository.search(searchMemberDto, pageable, searchType);
}

이렇게 메서드에 Pageable와 SearchType을 추가했다. 

 

이제 테스트를 해야하는데, 일단 기존의 테스트를 미리 수정해둔게 좀 있다. 

기존의 테스트에 전부 Pageable, SearchType을 넣어두었는데, 

private final PageRequest page = PageRequest.of(0, 3);

테스트를 위해 상수로 PageRequest를 만들어 썼으며,

memberService.search(searchMemberDto, page, EXACT)

 모든 메서드를 EXACT로 테스트했다. 

또한, 클래스명을 ~~~TEST_SEARCH_EXACT의 방식으로 클래스명을 수정했다. 

 

지금의 기준으로는, 정확한 이름을 넣어 검색했기에 EXACT로 했으며, 후에 CONTAINS를 위한 테스트를 만들 계획이다.

그건 다음편에서~~