본문 바로가기
JPA

[QueryDSL] BooleanExpression을 이용하여 동적쿼리 간결하게 작성하기

by 아리❣️ 2023. 2. 12.

QueryDSL을 이용하여 동적으로 쿼리를 작성해야 할 때가 있다.

이럴 때 발생할 수 있는 문제상황과 BooleanExpression을 이용한 리팩토링 방법을 알아보자.

 

 

문제상황 예시

 

게시판 글의 필터링 기능을 만든다고 상상해보자.

필터링할 수 있는 조건은 게시일, 작성자, 검색어, 카테고리 등등 매우 많다.

클라이언트는 필터링 정보를 쿼리파라미터에 담아 요청을 보낸다.

사용자가 딱히 필터링 정보를 선택하지 않았다면 쿼리파라미터 값은 null이다.

우리는 요청을 받아 쿼리파라미터에 데이터가 있는지 확인하고, 있다면 필터링하여 데이터를 반환한다.

 

단순히 해당 코드를 작성한다면 BooleanBuilder나 List<Predicate>를 이용할 수 있을 것이다.

public List<Object> find(Criteria criteria) {
    final List<Predicate> predicates = getPredicates(criteria);

    return queryFactory.selectFrom(table)
            .where(ExpressionUtils.allOf(predicates));
}

private List<Predicate> getPredicates(Criteria criteria) {
    final List<Predicate> predicates = new ArrayList<>();

	// A라는 파라미터가 빈값이 아니라면 조건 추가
    if (StringUtils.isNotEmpty(criteria.getA())) { 
        predicates.add(~~);
    }

	// B라는 파라미터가 빈값이 아니라면 조건 추가
    if (StringUtils.isNotEmpty(criteria.getB())) { 
        predicates.add(~~);
    }

	// C라는 파라미터가 빈값이 아니라면 조건 추가
    if (StringUtils.isNotEmpty(criteria.getC())) { 
        predicates.add(~~);
    }


    return predicates;
}

가볍게 예를 들자면 이렇다.

이때, 파라미터가 늘어난다면 getPredicates() 메서드 길이는 끝도 없이 늘어날 것이다.

주석이 없다면 어떤 정보를 어떻게 필터링하는 것인지 한눈에 알아보기 어렵다.

 

 

 

BooleanExpression을 이용한 리팩토링

 

각 파라미터별로 비어있다면 null을, 그렇지 않다면 BooleanExpression을 반환하는 메서드를 만든다.

null이 반환된다면 where절에서 자동으로 제외된다. 

 

public List<Object> find(Criteria criteria) {
    return queryFactory.selectFrom(table)
            .where(
            	eqA(criteria.getA()),
            	eqB(criteria.getB()),
                eqC(criteria.getC())
            );
}

public class BoardExpressions {

    public static BooleanExpression eqA(final String a) {
        if (StringUtils.isEmpty(a)) {
            return null;
        }

        return board.eq(a);
    }

    public static BooleanExpression eqB(final String b) {
        if (StringUtils.isEmpty(b)) {
            return null;
        }

        return board.eq(b);
    }

    public static BooleanExpression eqC(final String c) {
        if (StringUtils.isEmpty(c)) {
            return null;
        }

        return board.eq(c);
    }

}

 

 

 

이를 통해 얻을 수 있는 장점 

 

  • 메서드 이름을 통해 어떤 조건을 필터링하는지 표현할 수 있다.
  • 필터링하는 클래스를 따로 분리하여 추후 필터링 관련 로직이 변할 때 해당 클래스에만 변경하도록 구현할 수 있다.
  • 같은 조건식을 서로 다른 곳에서 이용하는 경우 재사용할 수 있다.