Zero to Hero
article thumbnail
Published 2021. 8. 28. 19:14
PageableExecutionUtils.getPage() Programming
 

PageableExecutionUtils (Spring Data Core 2.5.4 API)

 

docs.spring.io

PageableExecutionUtils는 Spring Data 모듈에 있는 추상 클래스다.

이 클래스에는 getPage()라는 메서드만이 있고, 웹 서비스에서 빈번히 제공해야 하는 Paging 기능을 제공한다.

public static <T> Page<T> getPage(List<T> content, Pageable pageable, LongSupplier totalSupplier)

메서드를 보면 3개의 인자를 받는다.

  1. content는 실제 page에 담길 데이터를 뜻한다.
  2. pageable은 paging 관련된 정보를 닮고 있는 Pageable 객체를 의미한다.
  3. totalSupplier는 내가 호출하는 데이터의 총개수를 반환하는 LongSupplier라는 Long 값을 반환하는 함수형 인터페이스다.
public Page<MemberTeamDto> searchPageComplexWithCountOptimization(MemberSearchCondition condition,
                                                                      Pageable pageable) {
     return PageableExecutionUtils.getPage(contents, pageable, countQuery::fetchCount);
}

대충 사용하면 이런 형태이다.

contents에는 JPA나 Querydsl을 사용한 실제로 반환할 데이터가 들어가고, pageable은 입력받은 Pageable 객체를 그대로 사용한다.

여기서 totalSupplier 자리에 countQuery::fetchCount라고 적혀있는데 풀어서 설명하면 countQuery.fetchCount()라는 메서드를 통째로 넣어준다고 이해하면 된다.

 

countQuery.fetchCount() 함수 자체를 넘기고 이 함수의 반환 값은 Long 타입이다. 그래서 LongSupplier 클래스인 것.

 

예를 들어서 총 데이터 개수가 150개이고, 한 페이지 사이즈가 50이고, 2번 페이지를 불러주세요라고 한다면(페이지가 1번부터 시작한다고 가정할 때) 150개의 데이터 중 51번 데이터 ~ 100번 데이터까지 만을 넣어서 반환해준다고 생각하면 되겠다. 여기서 중요한 점은 150개를 전부 읽은 뒤 51 ~ 100번 데이터를 반환하는 게 아니라 51 ~ 100번 데이터만을 읽어온다는 게 포인트다. 위 경우는 1번 페이지는 1번 ~ 50번, 2번 페이지는 51번 ~ 100번, 3번 페이지는 101번 ~ 150번까지의 데이터를 반환한다고 이해하면 된다.

 

또 해당 페이지의 데이터와 무관하게 전체 데이터가 몇 개인지도 알 수 있는데 이 또한 내부적으로 쿼리를 호출해한다. 그러니깐 paging기능을 사용하면 일반적으로 데이터를 조회하는데 1번, 데이터의 총개수를 세는데 1번 이렇게 쿼리가 나간다. 위의 예시를 사용하면 51번 ~ 100번까지 데이터를 호출하는 select 쿼리 1번, 모든 데이터 개수를 호출하는 select 쿼리 1번, 총 2번의 쿼리가 나간다.

 

한 페이지 사이즈를 10으로 설정할 때 1번 페이지(0번 부터 시작)에 해당하는 member 데이터를 반환하는 쿼리

아무튼 Spring과 조합해서 구현하면 이런 식의 결과를 얻을 수 있다.

member 데이터가 총 100개 있는 상태에서 한 페이지의 사이즈가 10이고 1번 페이지를 가져오는 것을 호출하면 위와 같은 결과를 얻을 수 있다. 아래쪽에 totalElements: 100이라고 보이는데 이 부분이 count query를 호출해서 얻는 정보라고 생각하면 된다.

 

실제로 쿼리가 2번 나갔다. 데이터 읽어오는데 1번, 전체 갯수 세는데 2번

여기서 전체 개수 세는 쿼리는 필요한 경우에만 나가게 최적화를 해주는데, 예를 들어 내가 퍼올리는 데이터 개수가 전체 데이터 개수보다 클 경우에는 퍼올린 데이터 개수가 전체 데이터 개수이기 때문에 굳이 count query를 날릴 필요가 없다. 이런 경우에 불필요한 count query를 날리지 않는다.

 

그럼 전체 데이터가 100개인데 페이지 1개 사이즈가 100이라면 모든 데이터를 퍼올린 거니깐 count query가 나가지 말아야 하는데...

이렇게 조건을 걸어서 날린다고 해보자. 전체 데이터가 100개인데 한 페이지 사이즈도 100이니깐 모든 데이터를 퍼올려서 count query가 나갈 필요가 없어야하는데...
실제론 2번이 나간다.

정확히는 보더에 걸리는, 그러니깐 전체 데이터 개수와 페이지 사이즈가 같은 경우에는 count query가 꼭 나간다.

이렇게 페이지 사이즈를 101로 맞춰주면?
조회 쿼리 1개만 나간다.

이유

getPage() 구현 코드를 보면 다음과 같다.

count query를 호출하지 않는 리턴문
count query를 호출하는 리턴문
count query를 호출하지 않는 리턴문의 조건

그냥 내부 구현에 >= 이 아니라 >로 되어있어서 페이지 사이즈와 데이터 길이가 같을 때도 count query가 나가는 것이었다.

 

'Programming' 카테고리의 다른 글

k8s commands 01  (0) 2021.10.23
Spring Batch Test 클래스 설정 방법  (0) 2021.10.07
N*N, N**2, pow(N, 2), math.pow(N,2)  (0) 2021.08.06
[2021 ver.] 서버 개발자 mac 장비 설정  (0) 2021.08.02
Mockito.doNothing()  (0) 2021.07.21
profile

Zero to Hero

@Doljae

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!