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

[스프링] 18. MemberRepository커스텀해서 검색기능 만들기

장아장 2023. 2. 4. 21:58

저번에 만든 멤버 리포지토리를 가지고, 이제 새로운 기능들을 주입시켜볼 것이다. 

기본적으로 메서드의 이름을 가지고 생성, 조회, 삭제를 할 수 있다.

 

그러면 조회하는 요소를 이름, 이메일, 닉네임중에 몇개를 골라서 수행하는, 동적 방식의 조회는 어떻게 할까?

 

  1. 이걸 JPQL(노가다)로 한번 만들어보고,
  2. 테스트를 수행시켜보고,
  3. QueryDSL을 통해 업그레이드 시켜볼 계획이다. 

일단, 검색을 시켜줄 dto와 예외처리부터 만들었다. 

@Data
public class SearchMemberDto {

    private String username;
    private String nickname;
    private String email;

    public SearchMemberDto(String username, String nickname, String email) {
        this.username = username;
        this.nickname = nickname;
        this.email = email;
    }
}

이중에서 만약 검색할 요소가 없다면, null로 처리해줄 것이다. 

 

public class NeedToAddSearchConditionException extends RuntimeException{
}
@ExceptionHandler(NeedToAddSearchConditionException.class)
@ResponseStatus(HttpStatus.NOT_ACCEPTABLE)
public Response needToAddSearchConditionException(){
    return Response.failure(404, "검색 조건을 하나라도 입력해야 합니다.");
}

이렇게 검색 조건이 없음을 예외처리시켰다. 

 

이렇게 하나의 인터페이스와, 하나의 클래스를 만들었다. 

리포지토리를 커스텀해서 만들기 위해선, 

이런식으로 리포지토리에 extends시긴 인터페이스에

기능을 만들어주고, 

이런식으로 Override해주어야 한다.

EntityManager를 이렇게 인스턴스로 두는 이유는, 쿼리문을 보내주는 역할을 하기 때문에 가져왔다. 

SpringBoot에서 EntityManager를 가져와 자동으로 클래스를 생성해줄 것이다. 

 

기능을 전체적으로 보여주자면, 

@Override
public List<Member> search(SearchMemberDto searchMemberDto) {
    StringBuilder query = new StringBuilder("select m from Member m ");
    if(searchMemberDto.getUsername() == null && searchMemberDto.getNickname() == null && searchMemberDto.getEmail() == null)
        throw new NeedToAddSearchConditionException();
    if(searchMemberDto.getUsername() == null && searchMemberDto.getNickname() == null)
        return em.createQuery("select m from Member m where m.email = :email", Member.class)
                .setParameter("email", searchMemberDto.getEmail()).getResultList();
    if(searchMemberDto.getNickname() == null && searchMemberDto.getEmail() == null)
        return em.createQuery("select m from Member m where m.username = :username", Member.class)
                .setParameter("username", searchMemberDto.getUsername()).getResultList();
    if(searchMemberDto.getUsername() == null && searchMemberDto.getEmail() == null)
        return em.createQuery("select m from Member m where m.nickname = :nickname", Member.class)
                .setParameter("nickname", searchMemberDto.getNickname()).getResultList();
    if(searchMemberDto.getUsername() == null)
        return em.createQuery("select m from Member m where m.nickname = :nickname and m.email = :email", Member.class)
                .setParameter("nickname", searchMemberDto.getNickname())
                .setParameter("email", searchMemberDto.getEmail())
                .getResultList();
    if(searchMemberDto.getNickname() == null)
        return em.createQuery("select m from Member m where m.username = :username and m.email = :email", Member.class)
                .setParameter("username", searchMemberDto.getUsername())
                .setParameter("email", searchMemberDto.getEmail())
                .getResultList();
    if(searchMemberDto.getEmail() == null)
        return em.createQuery("select m from Member m where m.username = :username and m.nickname = :nickname", Member.class)
                .setParameter("username", searchMemberDto.getUsername())
                .setParameter("nickname", searchMemberDto.getNickname())
                .getResultList();
    return em.createQuery("select m from Member m where m.username = :username " +
                    "and m.nickname = :nickname " +
                    "and m.email = :email", Member.class)
            .setParameter("username", searchMemberDto.getUsername())
            .setParameter("nickname", searchMemberDto.getNickname())
            .setParameter("email", searchMemberDto.getEmail())
            .getResultList();
}

이런식으로 노가다를 했다. 

사실 복붙을 했다.

이렇게 한 이유는, 나중에 Querydsl을 이용해 더 깔끔하게 바꿀 수 있기 때문이다. 

 

간단하게 기능을 설명하자면, null값을 제외하고 쿼리문을 만들어, 파라미터 설정을 해주고, 결과를 List<Member>로 반환해주는 것이다. 

모르겠다면 이걸 보고 오자. 쿼리 만드는 방식

 

이제 이걸 테스트해보았다. 하나만 예시로 보여주자면, 

private void createDatas() {
    for(int index = 0; index < 10; index++){
        RegisterRequestDto registerRequestDto = RegisterRequestDto.builder()
                .username("testUser" + index)
                .nickname("test" + index)
                .email("test" + index + "@test.com")
                .password("password" + index)
                .passwordCheck("password" + index)
                .build();
        Member member = new Member(registerRequestDto);
        memberRepository.save(member);
        em.flush();
        em.clear();
    }
}

@Test
@DisplayName("닉네임으로만 검색했을 때 검색한 멤버의 아이디가 같은 번호로 나온다. ")
public void searchByNickname() throws Exception{
    //given
    createDatas();

    SearchMemberDto searchMemberDto = new SearchMemberDto(null, "test3", null);

    //when
    List<Member> search = memberRepository.search(searchMemberDto);
    //then
    assertThat(search.get(0).getUsername()).isEqualTo("testUser3");
}

위의 메서드를 통해 10개정도 데이터를 만들어두고, 

아래의 메서드로 검색한 결과를 다른 인자값과 비교했다. 

모든 테스트는 정상적으로 동작했다. 

이제 이걸 더 깔끔하고, 재사용가능하며, 보기 좋게 만들어보자. 

다음편에선 Querydsl을 추가하고, 그 다음에 Querydsl로 프로젝트를 수정해봐야겠다.