QueryDSL - Pageable, PageRequest, Slice 에 대해서

구본식·2022년 9월 25일

1. 시작하기에 앞서

보통 조회 쿼리의 결과가 나올 때, 데이터가 너무 많게 되면 사용자가 보기에도 불편하고 후반부에 데이터들을 보기위해서 로딩에도 많은 시간이 소비되게 된다. 그렇기 때문에 대부분의 사이트들에서는 많은 데이터를 페이지로 나누어 처리하기 마련이다.
처리하는 방식에는 커서 기반 페이지네이션(무한스크롤)오프셋 기반 페이지네이션(흔이아는 offset,limit를 사용한)으로 나누어 진다.

이러한 페이징 기법에서 페이지의 offset, limit(가져올 데이터 개수, size), 페이징 인덱싱 계산들을 자동으로 해주는 기능을 Pageable 을 통해 쉽게 할수 있다.

2. PageRequest

PageRequest한페이지에 존재시킬 데이터 개수 size, 가져오길 원하는 페이지 번호, 정렬시킬 기준들을 인수로 받을수 있는 정적메서드들이 존재한다.

public static PageRequest of(int page, int size) {
		return of(page, size, Sort.unsorted());}

public static PageRequest of(int page, int size, Sort sort) {
	return new PageRequest(page, size, sort);}

public static PageRequest ofSize(int pageSize) {
	return PageRequest.of(0, pageSize);}

page 인덱싱을 직접 계산하여 사용해야 하지만 PageRequest에는 page,size를 이용하여 인덱싱을 자동으로 계산해준다. 그렇기 때문에 개발자는 page 인덱싱을 계산할 필요없이 가져올 page번호, size만 요청하면 되는것이다.

예를 들어 전체 데이터 개수가 50개라 하자. 전체 데이터를 다 가져오는 것이 아니라 처음 페이지 부터 10개만 가져오고 싶다하면 아래와 같이 메서드를 사용하면 된다.

Pageable pageable = PageRequest.of(0,10); //10개 씩

❗주위
PageRequest의 처음 page는 0를 의미

그리고 전체 데이터가 10개고 PageRequest(2,4) 이라면 요청한 데이터는 9~12 이지만 해당 page에 남은 데이터가 9~10이기 때문에 9~10 데이터만 반환하게 된다.

그리고 정렬 기준또한 아래와 같이 설정이 가능하다. 아래 방식뿐만 아니라 다른 방식도 존재

// 페이징, 정렬 기준 세팅하기
List<Sort.Order> orders = new ArrayList<>();
orders.add(Sort.Order.desc(searchForm.getOrderKey()));
Pageable pageable = PageRequest.of(0,3, Sort.by(orders)); 

위와 같은 방식을 사용해서 queryDSL에서 동적정렬이 가능하다.

3. Pageable

PageRequestPageable 인터페이스의 구현체를 상속받음

따라서 변경의 유연성을 높이기 위해 실제로 사용할때 아래와 같이PageRequest 보다는 상의 타입의 Pageable 타입을 사용한다.

Pageable pageable = PageRequest.of(0,3, Sort.by(orders)); 

4. Slice

PageRequest 객체를 통해 Repository에서 페이징 기능을 처리할 때 반환형으로
Page,Slice를 사용한다.
Page 는 게시판과 총 데이터 갯수가 필요한 환경에서 적합하다.
Slice 는 총 데이터 갯구는 필요없고 다음 페이지에 데이터가 존재하는지 여부만 필요할때 적합하다.

Spring data JPA를 사용하는 상황에서는(아래와같이)

Page<Member> findPageBy(Pageable pageable);
Slice<Member> findSliceBy(Pageable pageable);

Page의 경우에는 조회쿼리 이 후 전체 데이터 갯수를 한번더 조회하는 쿼리가 실행되고 Slice같은 경우에는 다음 페이지에 데이터가 있는지 유무만 알면되기때문에 `limit(size)+1'를 하여 조회하게 된다.

Spring data JPA를 사용하지 않고 JPA를 사용하는 경우에는 위와 같이 자동으
로 조회해주지 않기 때문에 개발자가 직접 기능을 아래 예시와 같이 작성해야된다.

public Slice<PostListDto> findPostsBySearchFormAboutAllUser(String lang, String title, Pageable pageable, BooleanBuilder builder) {
 JPAQuery<PostListDto> query = this.query.select(Projections.fields(PostListDto.class,
                        QPost.post.post_id,
                        QPost.post.title,
                        QUser.user.nickname,
                        QPost.post.language,
                        QPost.post.type,
                        QPost.post.likeNum,
                        QPost.post.replyNum))
                .from(QPost.post)
                .where(
                        LanguageEq(lang),
                        TitleContains(title),
                        builder //NoOffset 사용하기
                )
                .join(QPost.post.user, QUser.user);
 List<PostListDto> posts = query
                .limit(pageable.getPageSize() + 1)
                .fetch();
 return checkEndPage(pageable, posts);  }

 private Slice<PostListDto> checkEndPage(Pageable pageable, List<PostListDto> results) {
        boolean hasNext = false;
        if(results.size() > pageable.getPageSize()) {
            hasNext = true;
            results.remove(pageable.getPageSize()); //한개더 가져왔으니 더 가져온 데이터 삭제
        }
        return new SliceImpl<>(results, pageable, hasNext);
    }

위 코드는 JPA를 사용해서 Slice 사용할 때에 직접 다음 페이지에 데이터가 있는지 확인하는 로직을 추가한 모습이다.

profile
백엔드 개발자를 꿈꾸며 기록중💻

0개의 댓글