토이 프로젝트 배우게 된 것들 & 오류 해결🐰

Querydsl | Pageable과 page를 사용하는 방법

j_estory 2023. 1. 11. 17:24

SpringData에서 Pageable은 페이지 요청에 대한 데이터를 담을때 사용하는 인터페이스이다.

 

본론으로 들어가기 전에 위의 인터페이스에 대해 잠시 살펴보자!!

Pageable Interface

위의 사진과 같이 페이징 처리에 필요한 (limit, offset) 등을 구현하기 위한 메소드들이 정의되어 있으며,

pageable의 구현체인 AbstractPageRequest 클래스에 구현되어 있다.

 

pageable을 사용하기 위해서는 Controller 단에서 pageable 파라미터를 받으면 Spring MVC에서 

pageable 사용을 지원하기 때문에 자체적으로 PageableHandlerMethodArgumentResolver 클래스를 사용하여

pageable 인스턴스를 구현체인 PageRequest로 변환한다.

 

그래서 requestParameter로 page와 size를 아래와 같이 보내면 된다.

http://localhost:8080/api/review/list?accommodationSeq=2548&page=1&size=2

그렇다면!

queryDsl에서 pageable을 사용하여 어떻게 처리하면 될까 ?

 

 

✅ 첫 번째 방법.

 

 

PageImpl로 응답데이터 리턴

 

해당 방식은 아래와 같이 생성자에 conent 값, pageable 인터페이스, count 개수를 넣어주면 된다.

List<Review> reviews = jpaQueryFactory.selectFrom(QReview.review)
        .join(QReview.review.room, QRoom.room).fetchJoin()
        .where(QReview.review.room.roomSeq.in(roomseqList))
        .offset(pageable.getOffset())
        .limit(pageable.getPageSize())
        .fetch();

Long count = jpaQueryFactory.select(QReview.review.count())
        .from(QReview.review)
        .where(QReview.review.room.roomSeq.in(roomseqList))
        .fetchOne();

return new PageImpl<>(reviews,pageable,count);

참고 !

더보기

MySQL 기준으로 페이징 처리 시, 사용하는 키워드

- LIMIT : 행을 얼마나 가져올지

- OFFSET: 어디서 부터 가져올지

해당 방식은 단점이 존재한다.

카운트 쿼리는 Dto를 조회할 필요도 없고, 페이지 사이즈보다 전체 사이즈가 적은 경우라던가, 

마지막 페이지일 경우에는 굳이 카운트 쿼리를 날릴 필요가 없다.

이러한 단점을 보안한 방식이 다음 방식이다.

 

 

✅ 두 번째 방법.

 

 

count, contents 쿼리를 분리한 PageableExecutionUilts 사용

List<Review> fetch = jpaQueryFactory.selectFrom(QReview.review)
        .join(QReview.review.room, QRoom.room).fetchJoin()
        .where(QReview.review.room.roomSeq.in(roomseqList))
        .offset(pageable.getOffset())
        .limit(pageable.getPageSize())
        .fetch();

JPAQuery<Long> countQuery = jpaQueryFactory.select(QReview.review.count())
        .from(QReview.review)
        //.join(QReview.review.room, QRoom.room).fetchJoin()
        .where(QReview.review.room.roomSeq.in(roomseqList));


return PageableExecutionUtils.getPage(fetch, pageable, countQuery::fetchOne);

일단 첫번째로 fetch를 통해 contents를 가져오는 쿼리를 날리고,

count 쿼리는 fetch가 안붙어 있다.

쿼리를 분리해서 각각 날리기 때문에 카운트 쿼리에서 튜닝이 가능하다!

 

이는 앞선 방법의 단점을 보완하게 해준다.

마지막 인자로 카운트 쿼리 함수를 전달하는데, 내부 작동에 의해 전체 사이즈가 페이지 사이즈 보다 적은 경우나,

마지막 페이지일 경우 해당 함수를 실행하지 않는다.

즉! 불필요한 쿼리를 날리지 않게 된다. 

 

저의 프로젝트에서는 성능을 조금이나마 최적화 하기 위해 두번째 방식을 선택하였다!