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

[스프링] 20. QueryDsl을 이용해 MemberCustomRepository를 더 깨끗하게!!!

장아장 2023. 2. 5. 14:33

저번에 만든 QueryDsl을 어디에 써먹는지, 이제 적어볼 생각이다. 

 

저번에 만든 MemberCustomRepositoryImpl의 search를 전부 지워버렸다. 

이후, QueryDsl을 위해 만들어야 할 것들을 정리해보았다. 

  • JPAQueryFactory 인스턴스를 초기화 하는 생성자
  • search : 아이디, 닉네임, 이메일을 가지고 검색을 수행한다. null값은 스킵한다. 
    • usernameEq : 아이디가 null이면 null을 반환하고, 아니면 쿼리문을 반환한다. 
    • nicknameEq : 닉네임이 null이면 null을 반환하고, 아니면 쿼리문을 반환한다. 
    • emailEq : 이메일이 null이면 null을 반환하고, 아니면 쿼리문을 반환한다. 

이제 이걸 만들어보자. 

 

public class MemberCustomRepositoryImpl implements MemberCustomRepository {

    private final JPAQueryFactory query;

    public MemberCustomRepositoryImpl(EntityManager em){
        this.query = new JPAQueryFactory(em);
    }

이런식으로 클래스의 인스턴스와 생성자를 수정해주었다. 

그리고, @RequiredArgsConstructor를 삭제해주었다. 

MemberRepository에서 해당 클래스를 가져올 때, 스프링부트에서 EntityManager를 가져와 이 클래스를 생성해줄 것이다. 

JPAQueryFactory는 말 그대로, JPA에서 쿼리를 만들어주는 공장이다. 

쿼리문을 통해서 em.createQuery처럼 쿼리를 보내주어야 함으로 EntityManager를 이용해 인스턴스를 초기화해준다

(라고 머리속으로 이해했다.. 반박시 다맞음)

 

이후, 검색기능을 더 깔끔하게 만들어보았다. 

@Override
public List<Member> search(SearchMemberDto searchMemberDto) {
    if(searchMemberDto.getUsername() == null && searchMemberDto.getNickname() == null && searchMemberDto.getEmail() == null)
        throw new NeedToAddSearchConditionException();
    return query.selectFrom(QMember.member)
            .where(usernameEq(searchMemberDto.getUsername()),
                    nicknameEq(searchMemberDto.getNickname()),
                    emailEq(searchMemberDto.getEmail()))
            .fetch();
}

이젠 이것밖에 없다. 

기능을 훑어보면

  • 아이디, 닉네임, 이메일 전부가 null이면 예외처리하고, 
  • 값이 하나라도 있으면 리스트로 값을 반환하는데, 
  • 검색 결과가 없으면 빈 리스트를 반환한다. 

이렇게 이루어진다. 

 

그러면 usernameEq, nicknameEq, emailEq를 확인해봐야 한다. 

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

private BooleanExpression nicknameEq(String nickname){
    if(nickname == null || nickname.isEmpty() || nickname.isBlank()) return null;
    return QMember.member.nickname.eq(nickname);
}

private BooleanExpression emailEq(String email){
    if(email == null || email.isEmpty() || email.isBlank()) return null;
    return QMember.member.email.eq(email);
}

이런 식으로 만들어졌다. 

null값이거나, 문자열이 비어있거나, 공백문자로만 있으면 null을 반환하고, 문자열이 존재한다면, where안에 들어갈 쿼리문을 반환한다. 

 

null값을 무시하고 다음 쿼리문을 받을 수 있다는게 일단 첫번 째 좋은점이었던 것 같다. 

그리고, 이런 방식은 자바 문법으로 동작하기에, 오타나 잘못된 방식에 대해서 컴파일러 단위에서 에러가 발생한다. 

JPQL을 보면, 쿼리문법이 잘못되면 런타임중에만 문제를 확인할 수 있지, 컴파일 단위에서는 확인이 불가능하다. 

이게 꿀이 아니면 뭐냔 말임~~

 

이렇게 만들어진 코드를 가지고 저번에 만들어둔 테스트를 다시 돌려보았다.

아주 그냥 잘된다. 

queryDsl 아주 좋다. 하지만, 모든 것을 queryDsl로 만들 생각은 하지 않고 있다. 

동적인 검색이나, 편리한 방식을 위해서 써야지, 

그냥 멤버 전체 조회나, 간단한 동작은 JpaRepository를 통해 돌아가는 Spring Data JPA가 다 해주니까. 

 

이제, 이걸 결과 dto로 만들어주는 것도 추가하고, 서비스와 컨트롤러를 만들고나서, 다른 도메인들을 생각해보아야 겠다.