페이지네이션(Pagination)
클라이언트에서 데이터 요청 시, 특정 구간의 데이터만을 제공받을 수 있도록 하는 기법이다. 클라이언트에서는 요청 구간에 대한 정보를 넘겨주고, 서버에서는 해당 구간 정보에 맞춰 데이터를 잘라 응답으로 보내주게 된다. 인스타그램, 페이스북, 네이버 카페 등 “게시판” 기능이 있는 서비스라면 거의 모두 사용하는 기법이다.
(최소한의) 페이지네이션 개발에 필요한 정보
서버 단에서 데이터를 잘라내는 방법은 DB 쿼리 조회 시 시작점과 컨텐츠의 갯수를 이용해 OFFSET~LIMIT 구문을 활용하는 것이다.
SELECT * FROM [테이블]
OFFSET [시작점] LIMIT [보여줄 컨텐츠의 갯수]
한편, 화면에서 페이지를 표시하기 위해 총 페이지의 갯수 혹은 전체 컨텐츠의 갯수가 필요하다. 어느 쪽이든 결국 전체 컨텐츠의 갯수를 알아야 하므로, 개발 편의 상 둘 다 보내줘도 좋다. (만약 무한 스크롤 기법이라면 해당 부분은 필요하지 않다.)
// 전체 컨텐츠의 갯수
SELECT COUNT(*) FROM [테이블]
// 총 페이지의 갯수
// Math.ceil([전체 컨텐츠 갯수 / 한 페이지에 보여주고자 하는 컨텐츠 갯수])
Spring Data JPA
위와 같이 순수 쿼리로 구현할 수도 있지만, 벤더사에 따라 쿼리 문법이 달라지는 문제점이 있다(Oracle은 상당히 복잡하다). 그리고 페이지 수 계산, 예외 처리 로직 등 직접 처리하기 번거로운 로직들이 존재한다. 그리고 다음 페이지가 존재하는 지 여부 등 추가로 제공해주면 구현이 편리해지는 정보들을 같이 담아서 보내주고 싶을 수 있다. Spring Data JPA는 페이징 된 컨텐츠와 관련 정보들을 함께 담아서 보내주기 위해 추상화 된 페이지네이션 객체들을 제공한다.
Pageable과 PageRequest
페이지네이션에 필요한 정보를 담기 위한 인터페이스와 구현체이다. Pageable이 인터페이스고, PageRequest가 구현체이다. 다음과 같이 페이지 순서와 페이지의 크기를 넘겨줄 수 있고, 정렬도 수행할 수 있다.
// 페이지 순서(0부터 시작), 페이지의 크기
PageRequest.of(0,10)
// sort를 전달
PageRequest.of(0,10, Sort.by(Order.desc("price"))
Page와 Slice
Spring Data JPA 레포지토리에 Pageable을 전달하면, 반환 타입으로 Slice 혹은 Page를 받을 수 있다. 둘 모두 인터페이스이며, 페이지네이션을 통한 조회 결과를 저장하는 역할을 한다. 또한 Page는 Slice를 상속받는다.
Page가 전체 페이지 갯수를 담고 있는 반면, Slice는 그렇지 않다. 전체 데이터 count 조회 쿼리가 날아가지 않기 때문에 성능 측면에서 이점이 있다. 만약 무한 스크롤과 같이 전체 페이지 갯수가 굳이 필요하지 않다면 Slice를 사용하면 된다.
사용법
다음과 같이 Repository를 구현하고, 호출하면 된다.
Slice
public interface ItemRepository extends JpaRepository<Item, Long> {
Slice<Item> findSliceByPrice(int price, Pageable pageable);
}
Slice<Item> itemSlice = itemRepository.findSliceByPrice(5000, PageRequest.of(0,5));
Page
public interface ItemRepository extends JpaRepository<Item, Long> {
Page<Item> findPageBySlice(int price, Pageable pageable);
}
Page<Item> itemPage = itemRepository.findPageByPrice(5000, PageRequest.of(0,5));
참고 자료
'웹 개발 > Backend' 카테고리의 다른 글
| [Java / Spring] Spring Boot 테스트 작성하기 (2) | 2025.07.29 |
|---|---|
| [Python] 애플리케이션 서버 gunicorn (1) | 2025.06.15 |