Blog

[Spring][258] Page와 Slice를 통한 데이터 호출 제어

Category
Author
Tags
PinOnMain
1 more property
프로젝트에 수백만 건의 데이터를 불러오는 페이지가 있다. 그런데 이 모든 데이터를 불러오면 속도도 느리고 사용자 입장에서 이를 보는 경험도 좋지 않다. 그래서 일반적으로는 데이터를 불러올 때 일부만 불러오는 것이 일반적이다. 스프링에서는 Slice와 Page를 통해 이를 구현할 수 있다.
Page
JPA에서 데이터를 호출하면 Pageable을 기준으로 List를 생성하여 그 양식에 맞게 데이터를 저장한다. 데이터를 호출할 때 Pageable이라는 인스턴스를 생성해 입력해 주는데, Pageable에는 페이지 번호, 페이지 크기, 그리고 정렬 기준과 순서의 데이터를 갖는 Sort라는 객체를 필드로 갖는다.
Pageable을 통해 데이터를 호출하면, 전체 데이터를 PageSize만큼 자른 후, PageNumber번째의 데이터들을 불러오게 된다. PageNumber는 0부터 시작하여 순차적으로 올라가게 된다.
Page 객체를 통해 데이터를 불러오는 경우, 총 데이터 갯수를 함께 저장하여 총 Page 수를 계산하게 된다.
프로젝트에서 Page를 사용해 Book데이터를 구현하였다.
public Page<BookResponseDto> getAllBooksByCategoryOrKeyword3(String bookCategoryName, String keyword, int page) { QBook qBook = QBook.book; BooleanBuilder builder = new BooleanBuilder(); List<BookCategory> bookCategories = null; if (bookCategoryName != null) { BookCategory bookCategory = bookCategoryRepository.findByBookCategoryName(bookCategoryName); bookCategories = saveAllCategories(bookCategory); } if(keyword != null) builder.and(qBook.bookName.contains(keyword)); if(bookCategories != null) builder.and(qBook.bookCategory.in(bookCategories)); Sort sort = Sort.by(Sort.Direction.ASC, "bookId"); Pageable pageable = PageRequest.of(page, 20, sort); Page<BookResponseDto> bookList = bookRepository.findAll(builder, pageable).map(BookResponseDto::new); System.out.println(bookList.getTotalElements()); return bookList; }
Java
복사
전체 데이터 중 20개만 호출하는 결과를 볼 수 있다.
[Hibernate] select b1_0.book_id,b1_0.apply_id,b1_0.book_author,b1_0.book_category_id,b1_0.book_donation_event_id,b1_0.book_name,b1_0.book_publish,b1_0.rent_id,b1_0.book_status from book b1_0 order by b1_0.book_id limit ?,? [Hibernate] select count(b1_0.book_id) from book b1_0 4268m/s
SQL
복사
데이터를 불러오는 Query문과 전체 데이터의 크기를 불러오는 쿼리 총 두 번의 쿼리를 실행한다. 단순히 전체 데이터 중 상위 20개만 호출하는 코드임에도 소요시간이 상당했는데, 시간 소요 중 대부분은 전체 데이터의 갯수를 호출하는 데에 사용되었다.
Slice
Slice는 Page에서 total 필드 대신 hasNext 필드가 존재한다. 전체 데이터의 크기를 불러오는 대신, 불러온 데이터 다음의 데이터가 있는지 없는지만 판별하여 hasNext에 저장한다. 실제 코드로는 Page가 Slice를 확장시켜 만든 개념이다.
Page 사용 시 전체 갯수 조회에서 너무 많은 시간이 걸려 Slice를 사용해 보았다.
public Slice<BookResponseDto> getAllBooksByCategoryOrKeywordV4(String bookCategoryName, String keyword, int page) { QBook qBook = QBook.book; BooleanBuilder builder = new BooleanBuilder(); List<BookCategory> bookCategories = null; if (bookCategoryName != null) { BookCategory bookCategory = bookCategoryRepository.findByBookCategoryName(bookCategoryName); bookCategories = saveAllCategories(bookCategory); } if (keyword != null) builder.and(qBook.bookName.contains(keyword)); if (bookCategories != null) builder.and(qBook.bookCategory.in(bookCategories)); Sort sort = Sort.by(Sort.Direction.ASC, "bookId"); Pageable pageable = PageRequest.of(page, 20, sort); // Slice로 변경 Slice<BookResponseDto> bookList = customBookRepository.findAllSliceBooks(builder, pageable).map(BookResponseDto::new); System.out.println(bookList.hasNext()); return bookList; }
Java
복사
Page와 비슷하나 hasNext가 다음 페이지에도 데이터를 불러올 수 있음을 알려주고 있다.
[Hibernate] select b1_0.book_category_id,b1_0.book_category_isbn_code,b1_0.book_category_name,b1_0.parent_category_id from book_category b1_0 8m/s
SQL
복사
데이터만 호출하고 그 외의 Query문은 작성하지 않는 것을 볼 수 있다. 가장 많은 시간이 소비되었던 전체 데이터 갯수 호출이 사라져서 출력이 빠르게 된 것을 볼 수 있다.