Pagination 을 구현하는 방법들

GuruneLee·2022년 1월 16일
0

Let's Study 공부해요~

목록 보기
12/36

Pagination 이란?

상기한 UI 요소를 '페이지네이션(Pagination)' 이라고 부른다. 수많은 데이터 목록은 한 번에 불러오고 보여주기 어려우니, 그 일부만 가져와서 보여주는 방법이다. 이를 위해 DB에 저장된 데이터를 잘 자르는 것을 '페이징(Paging)' 이라 한다.

'페이지 번호'
'페이지당 출력할 데이터의 수'
'화면 하단에 출력할 페이지의 사이즈'
'검색 키워드'
'검색 유형'
등등...

페이지네이션을 위해 필요한 정보는 아주 많을 수 있다. 그러나 이 글에서는 페이지 번호(page)페이지당 출력할 데이터의 개수(size) 만 고려한 페이징에 대해 이야기하고자 한다.

어떻게 구현할 수 있을까?

물론 DB에서 모오든 데이터를 가져와서 (select * from table) 메모리상에서 순서대로 잘 끊어서 원하는 데이터만 골라낼 수도 있겠지만, 내가 설명하려는 페이징은 DB에서 원하는 데이터만 쿼리를 해오는 방법이다.
방법은 정말 여러가지이다. 그러나 본 게시물엔 필자가 이해한 것만 기록한다.

1. 직접 쿼리 만들어서 날리기
2. Spring-Data-Jpa 이용하기

LIMIT 을 이용해 직접 쿼리작성 (mysql)

MySQL 에서 LIMIT 구문은 데이터를 원하는 만큼 가져오고 싶을 때 사용한다.
LIMIT 의 첫 번째 파라미터는 시작위치를, 두 번째 파라미터는 가져올 데이터의 개수를 지정한다.

  • limit 3,2 : 3번째 데이터부터 두 개를 가져오겠다 (3번째, 4번째 데이터 가져옴)

따라서 우리가 원하는 데이터는 LIMIT (page-1)*size, size이다.

즉, page 와 size 가 주어졌을 때 SELECT * FROM table LIMIT (page-1)*size, size 쿼리를 날리면 원하는 데이터만 뽑아올 수 있다.

page=5, size=10 일때 쿼리예시

(spring 서버에 in-memory h2 db를 띄워서 콘솔에 직접 입력한 것이다.)

Spring Data Jpa 사용하기

Spring Data Jpa 을 이용하면

  1. Pageable 객체를 사용해 pagination 정보를 쉽게 관리할 수 있다.
  2. Jpa 쿼리 메서드의 파라미터로 Pageable 을 넘겨서 쉽게 DB에 쿼리를 날릴 수 있다.

(알아서 해줘서 편하다는 말)

Pageable 이란? (ft. PageRequest)

인터페이스다!!

Spring Data Jpa 에 정의되어 있는 Paeable 인터페이스엔 다음과 같은 아주 짧은 설명 주석이 붙어있다.

Pageable 이 Pagination 요청 정보를 담기위한 추상 인터페이스 라는 의미이다. 실제로 쓰기 위해서는 구현체가 필요하고, 물론 다~ 준비되어있다.

  • class QPageRequest : QueryDSL 을 위한 Pageable 구현체
  • class PageRequest : 가장 기본적인 Pageable 구현체
  • enum Unpaged : pagination 정보가 없는 것을 표현하기위한 구현체. INSTANCE 를 가지고 있다.

이 중 가장 기본이 되는 PageRequest 를 가장 많이 사용하게 된다.
(사실 이것도 귀찮다고 또 Spring 에서 만들어놓은 어노테이션이 있다. 그래서 이것도 많이 안쓴다..)

쿼리 메서드와 함께 쓰기

page 와 size 정보로 어찌저찌 Pageable 객체를 만들었다고 치고 이걸 쿼리메서드와 사용해보자.

JpaRepository 와 CrudRepository 사이에 있는 PagingAndSortingRepository 를 보면 Pageable 을 파라미터로 받는 메서드가 선언되어있다.

  • Page<T> findAll(Pageable pageable)

(Page 는 Pagination 에 필요한 정보를 담는 객체다. 요청받은 데이터들 외에 전체페이지수, 현재페이지번호 등 여러 부가정보를 담고있다.)

이걸 JpaRepository 가 상속받고, 또 이걸 우리의 커스텀 Repository로 상속해서 사용하게 된다.

//User 테이블을 페이징하는 메서드
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    Page<User> findAll(Pageable pageable);
}

(메서드 정의는 org.springframework.data.jpa.repository.support.SimpleJpaRepository.java 에 있다....ㅎ)

요청으로부터 Pageable 객체 만들기

드디어 Pageable 객체를 직접 만들어보자.
쿼리 파라미터로 page 와 size 가 오면 다음과 같은 방법으로 Pageable 객체를 만들 수 있다.

protected PageRequest(int page, int size, Sort sort) {
	super(page, size);
	Assert.notNull(sort, "Sort must not be null!");
	this.sort = sort;
}

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 of(int page, int size, Direction direction, String... properties) {
	return of(page, size, Sort.by(direction, properties));
}
  • PageReqeust.of() 라는 메서드에 page, size, sort 를 원하는대로 파라미터로 넘겨서 생성하면 된다.
// 사용 예시
Page<Entity> pleaseGiveMePages(int page, int size) {
	Pageable pageable = PageRequest.of(page,size);
    	Page<Entity> page = entityRepository.findAll(pageable);
        return page;
}

더 알면 좋은것들

  • 정렬을 하는 방법 (sort 를 넘기는 방법)
  • Page 객체의 구조
  • 컨트롤러에서 Pageable 을 파라미터로 받아오는 방법 (ft. @PageableDefault)
  • 인덱싱을 이용한 최적화
profile
Today, I Shoveled AGAIN....

2개의 댓글

comment-user-thumbnail
2022년 1월 18일

MySql의 LIMIT (page-1)*size, size 문법의 경우는 offset , limit의 문법과 같다고 생각하는데요. (즉, 몇 번째 ~ 몇 개를 조회한다.) 이는 offset 구간까지 테이블 full-scan이 진행되는 것으로 알고있어요. 만약 데이터가 10만건이라고 하고 마지막 페이지를 조회하기 위해서는 앞에서부터 모든 table의 full-scan이 진행된다고 알고 있습니다.

만약, 이 부분은 where문을 통해 where id > 이전 조회의 마지막 id를 이용한다면, 해당 부분에 대한 최적화를 진행할 수 있을 것 같네요! (이 경우네는 B-tree에서 나온 값을 가지고 빠르게 접근해서 페이징 조회를 진행할 수 있겠죠)

이 부분에 대해 참고자료 남겨봅니다!
https://dba.stackexchange.com/questions/261714/offset-vs-where-performance-for-pagination-with-index
https://jojoldu.tistory.com/528

1개의 답글