[Querydsl] Querydsl 동적으로 정렬하는 방법

2023. 3. 6. 21:04

게시글 조회 화면에서 검색타입, 정렬 방법에 따라 다른 결과를 반환해야 하는 기능을 구현하고 있었다.

단순한 검색 기능이면 스프링 데이터 JPA로 JPQL을 사용했겠지만, 이번 기능은 고려해야 할 부분이 많았기 때문에 Querydsl을 사용해서 구현해 보기로 했다.

 

where문 작성의 경우 BooleanExpression과 BooleanBuilder를 사용해서 간단하게 작성할 수 있었는데, 정렬 조건에 따른 작성을 어떻게 해야 하는지가 문제였다.

 

처음엔 service단에서 sort값에 따라 처리하도록 아래와 같이 작성했었다. 

//PostService
public Page<Post> findPosts(int page, int size, PostSearchDto postSearchDto, String sort) {
    PageRequest pageRequest = PageRequest.of(page, size);
    if (sort != null && sort.equals("Likes")) {
        return postRepository.getPostsSortByLikes(postSearchDto, pageRequest);
    } else {
        return postRepository.getPostsSortByNewest(postSearchDto, pageRequest);
    }
}

//PostRepositoryImpl

//최신순
@Override
public Page<Post> getPostsSortByNewest(PostSearchDto postSearchDto, Pageable pageable) {

    List<Post> results = queryFactory
            .select(post)
            .from(post)
            .leftJoin(post.pet, pet)
            .where(codeEq(postSearchDto.getCode()), typeEq(postSearchDto))
            .orderBy(post.id.desc())
            .offset(pageable.getOffset())
            .limit(pageable.getPageSize())
            .fetch();
	//...
}

//좋아요 순
@Override
public Page<Post> getPostsSortByLikes(PostSearchDto postSearchDto, Pageable pageable) {
    List<Post> results = queryFactory
            .select(post)
            .from(post)
            .leftJoin(post.pet, pet)
            .where(codeEq(postSearchDto.getCode()), typeEq(postSearchDto))
            .orderBy(post.likesCnt.desc())
            .orderBy(post.id.desc())
            .offset(pageable.getOffset())
            .limit(pageable.getPageSize())
            .fetch();
    //...
}

댓글 순까지 작성하면 orderBy를 제외한 다른 절들은 똑같은데 orderBy 때문에 3개의 메서드가 작성되는 게 별로였고 추후에 조건이 추가된다면 또 메서드를 만들어야 한다. 

 

OrderSpecifier

Querydsl은 동적으로 정렬조건을 적용할 수 있게 OrderSpecifier를 제공한다.

@Override
public Page<Post> getPosts(PostSearchDto postSearchDto, Pageable pageable, String sort) {
    List<Post> results = queryFactory
            .select(post)
            .from(post)
            .leftJoin(post.pet, pet).fetchJoin()
            .leftJoin(post.postComments, postComment)
            .where(codeEq(postSearchDto.getCode()), typeEq(postSearchDto))
            .orderBy(getOrderSpecifier(sort).stream().toArray(OrderSpecifier[]::new))
            .groupBy(post.id)
            .offset(pageable.getOffset())
            .limit(pageable.getPageSize())
            .fetch();
    
    //...
}

private List<OrderSpecifier> getOrderSpecifier(String sort) {
    List<OrderSpecifier> orders = new ArrayList<>();
    if (StringUtils.hasText(sort)) {
        Order direction = Order.DESC;
        switch (sort) {
            case "likes":
                orders.add(new OrderSpecifier(direction, post.likesCnt));
                orders.add(new OrderSpecifier(direction, post.id));
                break;
            case "comments":
                orders.add(new OrderSpecifier(direction, postComment.id.count()));
                orders.add(new OrderSpecifier(direction, post.id));
                break;
            case "newest":
                orders.add(new OrderSpecifier(direction, post.id));
                break;
            default:
                throw new BusinessLogicException(ExceptionCode.INVALID_SORT_TYPE);
        }
    }
    return orders;
}

위와 같이 List에 정렬 조건별로 OrderSpecifier를 추가해서 반환하는 메서드를 만들어 사용하면 정렬 조건이 추후에 조건이 바뀌거나 추가되어도 쉽게 적용이 가능하다. 

 

참고 블로그

https://dingdingmin-back-end-developer.tistory.com/entry/Springboot-JPA-Querydsl-%EB%8F%99%EC%A0%81-%EC%A0%95%EB%A0%AC-OrderSpecifier

 

Springboot JPA Querydsl 동적 정렬 OrderSpecifier

SQL 동적 정렬이란? 하나의 API에서 정렬 조건을 동적으로 변경해, 정렬 혹은 정렬 + 페이징을 진행하는 것을 의미합니다. 예시를 보며, 필요한 상황이 언제이며, 어떻게 해결하는지 알아가 보겠습

dingdingmin-back-end-developer.tistory.com

 

BELATED ARTICLES

more