๐ง ๋ค์ด๊ฐ๊ธฐ์
ํ์ด์ค๋ถ, ์ธ์คํ๊ทธ๋จ, ๋ค์ด๋ฒ ์นดํ ๋ฑ ์ฐ๋ฆฌ๊ฐ ์์ฃผ ์ฌ์ฉํ๋ ์๋น์ค๋ค์๋ ๊ณตํต์ ์ด ํ๋ ์๋ค. ๋ฐ๋ก ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ํ ๋ฒ์ ๋ณด์ฌ์ฃผ์ง ์๊ณ ํ์ํ ๋งํผ๋ง ์๋ผ์ ๋ณด์ฌ์ค๋ค๋ ์ ์ด๋ค. ์ฌ์ฉ์๋ ํ์ฌ ๋ณด๊ณ ์๋ ๋ฐ์ดํฐ๋ฅผ ๊ธฐ์ค์ผ๋ก "๋ค์", "์ด์ ", ๋๋ "ํน์ ํ์ด์ง"๋ฅผ ์์ฒญํ๊ณ ์์คํ ์ ์ด์ ๋ง๋ ๋ฐ์ดํฐ ์ผ๋ถ๋ง์ ์๋ตํ๋ค.
์ด๋ ๋จ์ํ UX์ ๋ฌธ์ ๋ง์ ์๋๋ค. ์์ฒ, ์๋ง, ์๋ฐฑ๋ง ๊ฑด์ ๋ฌํ๋ ๋ฐ์ดํฐ๋ฅผ ํ ๋ฒ์ ์๋ตํ๋ ์ผ์ ์๋ฒ์ ํฐ ๋ถํ๋ฅผ ์ฃผ๊ณ ์ฑ๋ฅ ์ ํ๋ ์ฅ์ ๋ก ์ด์ด์ง ์ ์๋ค. ๋ฐ๋ผ์ ์ ์ฌ์ง์ฒ๋ผ N๊ฐ์ ๋ฐ์ดํฐ๋ง ๋ณด์ฌ์ฃผ๊ณ , ์ฌ์ฉ์์ ์์ฒญ์ ๋ฐ๋ผ ๋ค์ ํน์ ์ด์ ์๋ฒ์ N๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ๋ณผ ์ ์๊ฒ ๊ตฌํํ๋ ๊ฒ์ ํตํด ์ด๋ฌํ ์ ์ ๋ณด์ํ ์ ์๋ค.
ํ์ง๋ง, ์์ ๊ฐ์ ๊ธฐ๋ฅ์ ๊ตฌํํ๊ธฐ ์ํด์๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ข ๋ฅ์ ๋ฐ๋ผ ์๋ก ๋ค๋ฅธ ๋ฌธ๋ฒ์ ์ฌ์ฉํด์ผ ํ๋ค๋ ๋ฒ๊ฑฐ๋ก์์ด ์๋ค.
SELECT * FROM posts LIMIT 10 OFFSET 20;
์๋ฅผ ๋ค์ด, MySQL์ ์์ ๊ฐ์ด LIMIT๊ณผ OFFSET์ ํ์ฉํ์ฌ ์์ ๊ฐ์ด ์์ฑํ๋ค.
SELECT * FROM (
SELECT a.*, ROWNUM rnum FROM (
SELECT * FROM posts
) a WHERE ROWNUM <= 30
) WHERE rnum > 20;
๋ฐ๋ฉด ORACLE์ ROWNUM์ ํ์ฉํ์ฌ ๋ณต์กํ ์๋ธ์ฟผ๋ฆฌ๋ฅผ ๊ตฌํํด์ผ ํ๋ค.
์ด์ฒ๋ผ DBMS๋ง๋ค ์๋ก ๋ค๋ฅธ ๋ฐฉ์์ผ๋ก ํ์ด์ง๊ตฌํ์ ์ฒ๋ฆฌํด์ผ ํ๊ธฐ ๋๋ฌธ์, ๊ฐ๋ฐ์๋ ๋งค๋ฒ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํน์ฑ์ ๊ณ ๋ คํด ์ฝ๋๋ฅผ ์์ฑํด์ผ ํ๊ณ , ์ ์ง๋ณด์๋ ๋ณต์กํด์ง๋ค.
List<Item> items = entityManager.createQuery("select i from Item i", Item.class)
.setFirstResult(0)
.setMaxResults(10)
.getResultList();
๊ทธ๋์ Spring์์๋ ์ด๋ฌํ ๋ฒ๊ฑฐ๋ก์์ ์์ ๊ธฐ ์ํด Pageable, Page ๋ฑ์ ๊ธฐ๋ฅ์ ํตํด ํ์ด์ง๋ค์ด์ ์ ์ถ์ํํ์ฌ ์ ๊ณตํ๋ค. JPA๋ฅผ ์ฌ์ฉํ๋ฉด ๋ณต์กํ SQL ์์ด๋ ์์ฝ๊ฒ ํ์ด์ง๋ค์ด์ ์ ๊ตฌํํ ์ ์์ผ๋ฉฐ, ์ด๋ค ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฌ์ฉํ๋๋ผ๋ ์ผ๊ด๋ ๋ฐฉ์์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๋๋ ์ ์กฐํํ ์ ์๋ค.
๐ธPageable
Spring Data JPA์์๋ ํ์ด์ง๋ค์ด์ ๊ธฐ๋ฅ์ ์์ฝ๊ฒ ๊ตฌํํ ์ ์๋๋ก Pageable์ด๋ผ๋ ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํ๋ค.
์ด Pageable์ ํตํด ์ฐ๋ฆฌ๋ ๋ช ๋ฒ์งธ ํ์ด์ง๋ฅผ ์กฐํํ ๊ฒ์ธ์ง, ํ ํ์ด์ง์ ๋ช ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ๋ด์ ๊ฒ์ธ์ง ๋ฑ์ ์ ๋ณด๋ฅผ ์์ฝ๊ฒ ์ ๋ฌํ ์ ์๋ค.
ํ์ง๋ง ๊ทธ ๋ด๋ถ๋ฅผ ์กฐ๊ธ ๋ ๋ค์ฌ๋ค๋ณด๋ฉด, ์ด Pageable์ ๋จ์ํ ์ธํฐํ์ด์ค๊ฐ ์๋๋ผ ๊ตฌ์ฒด ํด๋์ค๋ฅผ ํตํด ๊ตฌํ๋๊ณ ์๋ ์ถ์ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๊ณ ์๋ค.
์์ ๊ทธ๋ฆผ์ Pageable ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ ๊ณ์ธต ๊ตฌ์กฐ๋ฅผ ๋ณด์ฌ์ค๋ค:
- Pageable: ํ์ด์ง ์์ฒญ ์ ๋ณด๋ฅผ ์ ์ํ๋ ์ธํฐํ์ด์ค
- AbstractPageRequest: Pagable์ ๋ถ๋ถ์ ์ผ๋ก ๊ตฌํํ ์ถ์ ํด๋์ค
- PageRequest: ๊ตฌ์ฒด ๊ตฌํ์ฒด
์ด๋ฌํ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๊ณ ์๊ธฐ ๋๋ฌธ์ ๊ฐ๋ฐ์๋ ๊ตฌ์ฒด ๊ตฌํ์ฒด์ธ PageRequest ๊ฐ์ฒด๋ฅผ ํตํด ์ฝ๊ฒ Pagable์ ์์ฑํ ์ ์๋ค.
1๏ธโฃ PageRequest
PageRequest๋ Spring Data JPA์์ ํ์ด์ง ์ฒ๋ฆฌ์ ํ์ํ ์ ๋ณด๋ฅผ ๋ด๋ ๋ถ๋ณ ๊ฐ์ฒด
ํ์ด์ง๋ค์ด์ ์์ฒญ์ ๋ง๋ค๊ธฐ ์ํด ์ฌ์ฉํ๋ ๋ํ์ ์ธ ๊ตฌํ ํด๋์ค PageRequest์ ๊ตฌ์กฐ์ ๋ํด ์ดํด๋ณด๊ณ ์ ํ๋ค.
๐น์ฃผ์ ์์ฑ์ & ์ ์ ํฉํ ๋ฆฌ ๋ฉ์๋
public class PageRequest extends AbstractPageRequest {
private static final long serialVersionUID = -4541509938956089562L;
private final Sort sort;
protected PageRequest(int pageNumber, int pageSize, Sort sort) {
super(pageNumber, pageSize);
Assert.notNull(sort, "Sort must not be null");
this.sort = sort;
}
public static PageRequest of(int pageNumber, int pageSize) {
return of(pageNumber, pageSize, Sort.unsorted());
}
public static PageRequest of(int pageNumber, int pageSize, Sort sort) {
return new PageRequest(pageNumber, pageSize, sort);
}
public static PageRequest of(int pageNumber, int pageSize, Sort.Direction direction, String... properties) {
return of(pageNumber, pageSize, Sort.by(direction, properties));
}
...
}
์ฐ์ PageRequest๋ ํ๋๊ฐ์ผ๋ก Sort ๊ฐ์ฒด๋ฅผ ๊ฐ์ง๋ค.
๊ธฐ๋ณธ ์์ฑ์๋ pageNumber, pageSize, sort๋ฅผ ์ธ์๊ฐ์ผ๋ก ๋ฐ๊ณ pageNumber์ pageSize๋ ๋ถ๋ชจ ๊ฐ์ฒด์ ํ๋์ ์ ์ํด ์ค๋ค.
๊ธฐ๋ณธ ์์ฑ์๋ฅผ protected๋ก ์ ์ํจ์ผ๋ก์จ ์ธ๋ถ์์๋ ์ง์ ์์ฑํ ์ ์๊ณ ์ ์ ๋ฉ์๋๋ฅผ ํตํด์๋ง ์์ฑํ ์ ์๋ค
์ด๋, ์ ํฉ๋ฉ์ ์ข ๋ฅ๋ ๋ค์๊ณผ ๊ฐ๋ค.
Pagable ๊ด๋ จ ์ ์ ํฉํ ๋ฆฌ ๋ฉ์๋๋ค์ ํ์ด์ง ํ ๋ฐ์ดํฐ์ ๊ฐ์๋ฟ๋ง ์๋๋ผ, ํ์ด์ง ์ ๋ ฌ ์กฐ๊ฑด(Sort)๊น์ง ํจ๊ป ์ง์ ํ ์ ์๋ค.
1.pageNubmer + pageSize
PageRequest request = PageRequest.of(0, 10)
pageNumber์ size๋ก PageRequest๊ฐ์ฒด๋ฅผ ์์ฑํ ์ ์๋ค.

Pageable ์ธํฐํ์ด์ค์ ofSize() ๋ฉ์๋๋ฅผ ํตํด์๋ Pageable ๊ฐ์ฒด๋ฅผ ์์ฑํ ์ ์๋ค.
ํ์ง๋ง ์ด๋ ์ฃผ์ํด์ผ ํ ์ ์ pageSize๋ง ์ธ์๊ฐ์ผ๋ก ์ ๋ฌ๋ฐ๊ธฐ์ default pageNumber index๊ฐ 0์ผ๋ก ์ค์ ๋์ด ์์์ ํ์ธํ ์ ์๋ค.
2. page Number + pageSize + sort
Sort sort = Sort.by("createdAt");
PageRequest request = PageRequest.of(0, 10, sort);
PageRequest request = PageRequest.of(0, 10, Sort.by(Direction.DESC, "createdAt"));
Sort.by()์ ์ํฐํฐ ํ๋๋ช ์ ์ ์ํจ์ผ๋ก์จ ์ ๋ ฌ์ ์ํํ ์ ์๋ค.
3. pageNumber + pageSize + direction + properties
PageRequest request = PageRequest.of(0, 10, Sort.Direction.DESC, "createdAt");
Sort๊ฐ์ฒด๋ฅผ ์ง์ ์ ์ํ์ง ์์๋ ํ๋๋ช ์ ์ธ์๊ฐ์ผ๋ก ๋๊ฒจ์ค์ผ๋ก์จ ์ ๋ ฌ์ ์ํํ ์ ์๋ค.
๐นํ์ด์ง ๊ด๋ จ ๋ฉ์๋
public PageRequest next() // ๋ค์ ํ์ด์ง ์์ฒญ ๊ฐ์ฒด ์์ฑ
public PageRequest previous() // ์ด์ ํ์ด์ง ์์ฒญ ๊ฐ์ฒด ์์ฑ (0์ด๋ฉด ๊ทธ๋๋ก)
public PageRequest first() // ์ฒซ ํ์ด์ง ์์ฒญ ๊ฐ์ฒด ์์ฑ
public PageRequest withPage(int pageNumber) // ์ํ๋ ํ์ด์ง๋ก ์ด๋
๐น์ ๋ ฌ ์ ๋ณด ๋ณ๊ฒฝ
public PageRequest withSort(Sort.Direction direction, String... properties)
public PageRequest withSort(Sort sort)
์์ ๊ฐ์ ๋ฉ์๋๋ค์ ์๋ก์ด PageRequest ๊ฐ์ฒด๋ฅผ ์์ฑํด์ return ํด์ค๋ค.
=> PageRequest๋ ๋ถ๋ณ๊ฐ์ฒด์ด๊ธฐ ๋๋ฌธ!
2๏ธโฃAbstractPageRequest
์์ ๋ณธ PageRequest์ ๊ธฐ๋ณธ์์ฑ์์์ super๋ก ๋๊ฒจ์ฃผ๋ pageNumber์ pageSize๊ฐ AbstractPageRequest์ ํ๋๋ก ์ ์๋์ด ์์์ ํ์ธํ ์ ์๋ค.
๋ํ, ์์์ ๋ณธ PageRequest์ ํ์ด์ง ๊ด๋ จ ๋ฉ์๋๋ค์ AbstractPageRequest์ ๋ฉ์๋๋ฅผ ์ค๋ฒ๋ผ์ด๋ํ ๊ฒ์ ํ์ธํ ์ ์๊ณ return type์ด Pageable ์ธํฐํ์ด์ค๋ก ๊ฐ์ธ์ ธ ์๋ค๋ ๊ฒ์ ํ์ธํ ์ ์๋ค!
๐ธPage & Slice
์์ ์ดํด๋ณธ Pageable์ ํ์ด์ง ์์ฒญ ์ ๋ณด๋ฅผ ๋ด๋ ์ธํฐํ์ด์ค๋ค.
ํ์ง๋ง ์ด๋ ์ด๋๊น์ง๋ ์์ฒญ์ ์
์ฅ์์์ ์
๋ ฅ ๊ฐ์ผ ๋ฟ, ๊ฐ๋ฐ์๊ฐ ๊ถ๊ทน์ ์ผ๋ก ์ํํด์ผ ํ๋ ๋ถ๋ถ์ ์์ฒญ์ ๋ง๋ ๋ฐ์ดํฐ๋ฅผ ํ์ด์ง ๋จ์๋ก ๊ฐ์ธ์ ์ ๋ฌํด ์ฃผ๋ ๊ฒ์ด๋ค.
ํ์ด์ง์ ์งํํ ๋ฐ์ดํฐ์ ์๋ณธ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์๋ค.
๋ฐ๋ผ์ JPA๋ Pageable ์ ๋ณด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก DB์ ์ ์ ํ ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆฌ๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ค์ ํน์ ๊ฐ์ฒด๋ก ๊ฐ์ธ์ ๋ฐํํด์ค์ผ ํ๋ค.
์ด๋ ์์ฒญ ๊ฒฐ๊ณผ๋ฅผ ๋ด์์ฃผ๋ ๊ฐ์ฒด๊ฐ ๋ฐ๋ก Page์ Slice๋ค.
ํด๋น ๊ฐ์ฒด๋ค์ ๋ฐ์ดํฐ ๋ฆฌ์คํธ๋ฟ๋ง ์๋๋ผ, ๋ค์ ํ์ด์ง๊ฐ ์๋์ง, ์ ์ฒด ํ์ด์ง ์๋ ๋ช ๊ฐ์ธ์ง ๋ฑ ํ์ด์ง์ ํ์ํ ๋ฉํ ์ ๋ณด๋ฅผ ํจ๊ป ์ ๊ณตํด ์ค๋ค.
Page์ Slice์ ๊ฐ์ฒด์ ๋ด๋ถ๊ตฌ์กฐ๋ฅผ ์ดํด๋ณด๋ฉด์ ์ฐจ์ด์ ์ ์์๋ณด๊ณ ๊ทธ์ ๋ฐ๋ฅธ ์ฌ์ฉํ๊ธฐ์ ์ ์ ํ ์ํฉ์ ๋ํ ์ธ์ฌ์ดํธ๊น์ง ๋์ถํด ๋ณผ ์์ ์ด๋ค.
1๏ธโฃ ๊ฐ์ฒด๋ค ๊ฐ์ ์์๊ด๊ณ
๊ฐ์ฒด๋ค๊ฐ์ ์์๊ด๊ณ๋ฅผ ์ดํด๋ณด๋ฉด ์์ ๊ฐ์ ๊ตฌ์กฐ์์ ํ์ธํ ์ ์๋ค.
๐น1. Slice
"๋ค์ ํ์ด์ง๊ฐ ์๋์ง"๋ง ์๋ ค์ฃผ๋ ๊ฐ๋ฒผ์ด ํ์ด์ง ๊ฒฐ๊ณผ ๊ฐ์ฒด
๐น2. Page
Slice์ ๋ชจ๋ ๊ธฐ๋ฅ + ์ ์ฒด ํ์ด์ง ์, ์ด ๋ฐ์ดํฐ ๊ฐ์๊น์ง ์ ๊ณต
๐น3. Chunk
Slice์ ๊ณตํต ๊ตฌํ์ ๋ด๋นํ๋ ์ถ์ ํด๋์ค
๐น4. SliceImpl
Slice<T>๋ฅผ ๊ตฌํํ ์ค์ ํด๋์ค
๐น5.PageImpl
Page<T>๋ฅผ ์ค์ ๋ก ๊ตฌํํ ๋ํ ํด๋์ค
๐น6. GeoPage
์ง๋ฆฌ ๊ธฐ๋ฐ ๊ฒ์ ๊ฒฐ๊ณผ๋ฅผ ๋ด๋ PageImpl<GeoResult<T>>์ ํ์ฅ ํด๋์ค.
Page๊ธฐ๋ฅ + ๊ฐ ๊ฒฐ๊ณผ์ ๊ฑฐ๋ฆฌ ์ ๋ณด + ํ๊ท ๊ฑฐ๋ฆฌ
๊ฐ๊ฐ์ ํด๋์ค์ ๋ด๋ถ๋์์ ๋ํด์๋ Page์ Slice๋ฅผ ์ค์ฌ์ผ๋ก ์ค๋ช ํ๊ณ ์ ํ๋ค.
2๏ธโฃ Page
Page ๊ฐ์ฒด๋ PageImpl ๊ตฌํ ํด๋์ค๋ฅผ ํตํด ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ ๋์์ ์ํํ๋ค.
spring-data-commons/src/main/java/org/springframework/data/domain/PageImpl.java at main · spring-projects/spring-data-commons
Spring Data Commons. Interfaces and code shared between the various datastore specific implementations. - spring-projects/spring-data-commons
github.com
์์ ๊ฐ์ด PageImpl์ ์์ฑ์๋ฅผ ์ดํด๋ณด๋ฉด ๋ ๊ฐ์ง ๋ฐฉ์์ผ๋ก Page๋ฅผ ์์ฑํ ์ ์์์ ํ์ธํ ์ ์๋ค.
(content์ pagable์ Chunk ๊ฐ์ฒด์์ ๊ด๋ฆฌํ๋ค.)
๐น์์ฑ์ 1. List<T>content + pageable + total
// ํ์ฌ ํ์ด์ง์ ๋ฐ์ดํฐ
List<String> content = Arrays.asList("item1", "item2", "item3");
// Pageable ์ ๋ณด (0๋ฒ ํ์ด์ง, 3๊ฐ์ฉ ์กฐํ)
Pageable pageable = PageRequest.of(0, 3);
// ์ ์ฒด ๋ฐ์ดํฐ ๊ฐ์๊ฐ 100๊ฐ๋ผ๊ณ ๊ฐ์
long total = 100
// Page ์์ฑ
Page<String> page = new PageImple<>(content, pageable, total);
์ ์ฒด ํ์ด์ง ์ ๋ณด๋ฅผ ์ง์ ์ ๋ฌํ๋ ๊ฐ์ฅ ์ผ๋ฐ์ ์ด๊ณ ๋ํ์ ์ธ ์์ฑ์์ด๋ค.
- content: ํ์ฌ ํ์ด์ง์ ํด๋นํ๋ ๋ฐ์ดํฐ ๋ชฉ๋ก
- pageable: ์์ฒญ ์ ๋ณด
- total: ์ ์ฒด ๋ฐ์ดํฐ ๊ฐ์
์ ์ฒด ๋ฐ์ดํฐ ๊ฐ์๋ฅผ ์์์ผ ํ๊ธฐ ๋๋ฌธ์, Spring Data JPA๋ ๋ด๋ถ์ ์ผ๋ก count(*) ์ฟผ๋ฆฌ๋ฅผ ๋ณ๋๋ก ์คํํ์ฌ ์ ์ฒด ๋ฐ์ดํฐ ์๋ฅผ ์กฐํํ๋ค. => "Select ์ฟผ๋ฆฌ๊ฐ ๋๊ฐ๋ค"
๐น์์ฑ์ 2. List<T>content
List<String> content = userRepository.findAll() // ์ ์ฒด ๋ฐ์ดํฐ
Page<User> page = new PageImpl<>(content); // ์ ์ฒด๋ฅผ Page๋ก ๊ฐ์ธ๊ธฐ
ํ์ด์ง ์ ๋ณด ์์ด, ๋จ์ํ ๋ฆฌ์คํธ๋ง ์ฃผ์ด์ง ๊ฒฝ์ฐ์ ์ฌ์ฉํ๋ ๊ฐ๋จํ ์์ฑ์์ด๋ค.
๋ด๋ถ์ ์ผ๋ก Pageable.unpaged()๋ฅผ ์ฌ์ฉํ๊ณ , ์ด ๊ฐ์๋ content.size()๋ก ๋์ฒดํ๋ค.
๐น๋ฉ์๋
๋ํ PageImpl์์๋ ์์ ๊ฐ์ด hasNext(), isLast(), map() ๋ฉ์๋์ ๋ํด ์ ๊ณตํด ์ค๋ค.
(getTotalPages()์ getTotalElements()๋ Page ๋ฉ์๋๋ฅผ override ํ ๋ฉ์๋์ด๋ค.)
getTotalPages()์ getTotalElements() ๋ฉ์๋๋ Slice์ ๋ฌ๋ฆฌ Page ๊ฐ์ฒด๋ง ๊ฐ์ง๊ณ ์๋ ๋ฉ์๋์ด๋ค.
- hasNext(): ๋ค์ ํ์ด์ง๊ฐ ์๋์ง์ ๋ํ ์ฌ๋ถ
- isLast(): ํ์ฌ ํ์ด์ง๊ฐ ๋ง์ง๋ง์ธ์ง์ ๋ํ ์ฌ๋ถ
3๏ธโฃ Slice
Slice๊ฐ์ฒด๋ SliceImpl ๊ตฌํ ํด๋์ค๋ฅผ ํตํด ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ ๋์์ ์ํํ๋ค.
spring-data-commons/src/main/java/org/springframework/data/domain/SliceImpl.java at main · spring-projects/spring-data-commons
Spring Data Commons. Interfaces and code shared between the various datastore specific implementations. - spring-projects/spring-data-commons
github.com
SliceImpl ํด๋์ค๋ ๋ง์ฐฌ๊ฐ์ง๋ก ๋ ๊ฐ์ง์ ์์ฑ์๋ฅผ ์ ๊ณตํด ์ค๋ค.
๐น์์ฑ์ 1. List<T>content + pageable + hasNext
// ํ์ฌ ํ์ด์ง์ ๋ฐ์ดํฐ
List<String> content = Arrays.asList("item1", "item2", "item3");
// Pageable ์ ๋ณด (0๋ฒ ํ์ด์ง, 3๊ฐ์ฉ ์กฐํ)
Pageable pageable = PageRequest.of(0, 3);
// ๋ค์ ํ์ด์ง๊ฐ ์๋ค๊ณ ๊ฐ์
boolean hasNext = true;
// SliceImpl ์์ฑ
Slice<String> slice = new SliceImpl<>(content, pageable, hasNext);
์ฌ๋ผ์ด์ค ํ์ด์ง ๊ฒฐ๊ณผ๋ฅผ ์์ฑํ ์ ์๊ฒ ํด์ฃผ๋ ์ฃผ์ ์์ฑ์์ด๋ค.
- content: ํ์ฌ ํ์ด์ง์ ํด๋นํ๋ ๋ฐ์ดํฐ ๋ชฉ๋ก
- pageable: ์์ฒญ ์ ๋ณด
- hasNext: ๋ค์ ํ์ด์ง์ ์ฌ๋ถ
๐จ Page๊ฐ์ฒด์์ ์ฐจ์ด์
Page ๊ฐ์ฒด์๋ ๋ฌ๋ฆฌ, Slice๋ total์ด ์๋ hasNext ๊ฐ์ ์ธ์๋ก ๋ฐ๋๋ค.
์ฆ, ์ ์ฒด ๋ฐ์ดํฐ ๊ฐ์๋ฅผ ์๊ธฐ ์ํ ๋ณ๋์ COUNT(*) ์กฐํ ์ฟผ๋ฆฌ๊ฐ ํ์ํ์ง ์๋ค.
๋ฐ๋ผ์ ์ ์ฒด ํ์ด์ง ์๋ฅผ ์ ํ์๊ฐ ์๋ ๋ฌดํ ์คํฌ๋กค๊ณผ ๊ฐ์ ๊ฒฝ์ฐ, ๋ถํ์ํ ์ฟผ๋ฆฌ ๋ถ๋ด์ ์ค์ผ ์ ์์ด Slice๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ ํฉํ๋ค.
๐ง ์ ์ฒดํ์ด์ง๋ฅผ ์กฐํํ์ง ์๋๋ฐ ์ด๋ป๊ฒ hasNext()๋ฅผ ํ๋ณํ ์ ์๋๊ฐ?
์ ๋ง ์ฌ๋ฏธ์๋ ๋ถ๋ถ์ด์๋ค.
Slice๋ ๋ณ๋์ COUNT(*) ์กฐํ ์ฟผ๋ฆฌ๋ฅผ ์คํํ์ง ์์์ ์ ์ฒด ํ์ด์ง ๊ฐ์๋ฅผ ์ ์ ์๋ค.
๊ทธ๋ฌ๋ฉด PageRequest๊ฐ์ ๋ฐ์์ ๋ ์ด๋ป๊ฒ ํ์ฌ ํ์ด์ง๊ฐ ๋ง์ง๋ง ํ์ด์ง์ธ์ง ์ ์ ์์๊น?
PageRequest pageRequest = PageRequest.of(0, 10); // 0ํ์ด์ง 10๊ฐ์ฉ
Slice<User> users = userRepository.findBySomething(..., pageRequest);
์์ ๊ฐ์ด Slice ๊ฐ์ฒด๋ฅผ ๋ฐํ๋ฐ๊ณ ์ ํ ๋ ๋๊ฐ๋ ์ฟผ๋ฆฌ๋ฅผ ํ์ธํด๋ณด์.
select
user0_.id as id1_0_,
user0_.name as name2_0_,
user0_.age as age3_0_
from
user user0_
where
user0_.age > ?
order by
user0_.id asc
limit ? offset ?
Hibernate log ์์ผ๋ก๋ ์์ ๊ฐ์ด ๋๊ฐ๊ฒ ๋๋ค.
์ด๋ ์ค์ํ ๋ถ๋ถ์ ?์ ๋ค์ด๊ฐ๋ ๊ฐ์ด๋ค. ์ด๋ฅผ ์์๋ณด๊ธฐ ์ํด์๋ p6spy ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํตํด์ ์ดํด๋ณด์.
SELECT
user0_.id,
user0_.name,
user0_.age
FROM
user user0_
WHERE
user0_.age > 20
LIMIT 11 OFFSET 0;
์ด๋ ๊ฒ ๋ก๊ณ ๊ฐ ์ฐํ๋ ๊ฒ์ ์ดํด๋ณด๋ฉด ํน์ดํ ์ ์ ํ์ธํ ์ ์๋ค.
์์ ๋ณธ PageRequest์์๋ ํ์ด์ง ์ฌ์ด์ฆ๋ฅผ 10์ผ๋ก ์กฐํํ์๋๋ฐ ๋ก๊ณ ์์๋ 10 + 1์ธ 11๋ก ์ฐํ์๋ค.
๊ธฐ์กด์ ์กฐํํ๊ณ ์ ํ๋ ์ฌ์ด์ฆ๋ณด๋ค ํ ๊ฐ๋ฅผ ๋ ๊ฐ์ ธ์ค๋ ์๋ฆฌ๋ ๋ฌด์์ผ๊น?
๋ง์ฝ 10๊ฐ์ฉ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์จ๋ค๋ฉด ์ ์ฒด ๊ฐ์๋ฅผ ์กฐํํ๋ ๊ฒ ์๋ ์ด์ ๋ค์์ ํ์ด์ง ์ฌ๋ถ์ ๋ํด์๋ ์๊ธฐ ํ๋ค ๊ฒ์ด๋ค.
๊ทธ๋ฌ๋ ๋ง์ฝ ๊ธฐ์กด์ ํ์ด์ง ์ฌ์ด์ฆ๋ณด๋ค ํ ๊ฐ๋ฅผ ๋ํด์ ๋ถ๋ฌ์จ๋ค๋ฉด ๋ค์ ํ์ด์ง์ ๋ฐ์ดํฐ๊ฐ ์๋์ง์ ๋ํ ์ฌ๋ถ๋ฅผ ํ์ธํ ์ ์๋ค๋ ์ ์ด๋ค. ๋ฐ๋ผ์ JPA์์๋ ์ด ์ ์ ์ด์ฉํ์ฌ ์ฟผ๋ฆฌ๋ฅผ ๋ณด๋ผ ๋ PageRequest๊ฐ ์๊ตฌํ๋ ํ์ด์ง ์ฌ์ด์ฆ์ +1์ ํ์ฌ ์ฟผ๋ฆฌ๋ฅผ ์กฐํํ๊ณ , ๋ค์ ํ์ด์ง์ ์ฌ๋ถ๋ฅผ ํ์ธํ๋ค.
๐น์์ฑ์ 2. List<T>content
List<String> content = userRepository.findAll() // ์ ์ฒด ๋ฐ์ดํฐ
Slice<User> slice = new SliceImpl<>(content); // ์ ์ฒด๋ฅผ Slice๋ก ๊ฐ์ธ๊ธฐ
PageImpl์ ๋ ๋ฒ์งธ ์์ฑ์์ ๋ง์ฐฌ๊ฐ์ง๋ก ๊ฐ๋จํ ์ฉ๋์ ์์ฑ์๋ก, pageable์ด ํ์ ์๋ ๊ฒฝ์ฐ์ ์ฌ์ฉํ๋ค.
this(content, Pageable.unpaged(), false);
์ด๋, ๋ด๋ถ ๋์์ hasNext๋ฅผ false๋ก ์ฌ์ฉํ๋ค.
๐นSlice ๋ฉ์๋
int getNumber(); // ํ์ฌ ํ์ด์ง ๋ฒํธ (0๋ถํฐ ์์)
int getSize(); // ํ ํ์ด์ง๋น ๋ฐ์ดํฐ ๊ฐ์
int getNumberOfElements(); // ํ์ฌ ํ์ด์ง์ ํฌํจ๋ ์ค์ ๋ฐ์ดํฐ ์
List<T> getContent(); // ํ์ฌ ํ์ด์ง์ ๋ฐ์ดํฐ ๋ชฉ๋ก
boolean hasContent(); // ๋ฐ์ดํฐ๊ฐ ์กด์ฌํ๋์ง ์ฌ๋ถ
Sort getSort(); // ์ ๋ ฌ ์ ๋ณด ๋ฐํ
boolean isFirst(); // ์ฒซ ํ์ด์ง์ธ์ง ์ฌ๋ถ
boolean isLast(); // ๋ง์ง๋ง ํ์ด์ง์ธ์ง ์ฌ๋ถ
boolean hasNext(); // ๋ค์ ํ์ด์ง๊ฐ ์๋์ง ์ฌ๋ถ
boolean hasPrevious(); // ์ด์ ํ์ด์ง๊ฐ ์๋์ง ์ฌ๋ถ
Pageable getPageable(); // ํ์ฌ ํ์ด์ง์ Pageable ์ ๋ณด
Pageable nextPageable(); // ๋ค์ ํ์ด์ง์ Pageable ์ ๋ณด
Pageable previousPageable(); // ์ด์ ํ์ด์ง์ Pageable ์ ๋ณด
<U> Slice<U> map(Function<? super T, ? extends U> converter); // content๋ฅผ ๋ณํํด ์๋ก์ด Slice ์์ฑ
Pageable nextOrLastPageable(); // ๋ค์ ํ์ด์ง ์์ผ๋ฉด next, ์์ผ๋ฉด ํ์ฌ Pageable ๋ฐํ
Pageable previousOrFirstPageable(); // ์ด์ ํ์ด์ง ์์ผ๋ฉด previous, ์์ผ๋ฉด ํ์ฌ Pageable ๋ฐํ
SliceImpl์์๋ ๋ณ๋๋ก ๊ตฌํ๋ ๋ฉ์๋๋ ์์ผ๋ฉฐ, Slice ์ธํฐํ์ด์ค์์ ์ ์๋ ๋ฉ์๋๋ค์ Chunk ์ถ์ ํด๋์ค๊ฐ ๊ณตํต์ผ๋ก ๊ตฌํํ๊ณ ์๋ค.
๋ฐ๋ผ์ ์์ ์ดํด๋ณธ getContent(), hasNext(), isFirst()์ ๊ฐ์ ๋ฉ์๋๋ค์ Slice๋ฟ๋ง ์๋๋ผ Page ๊ฐ์ฒด์์๋ ๋์ผํ๊ฒ ์ฌ์ฉํ ์ ์๋ค. ์ด๋ PageImpl ์ญ์ Chunk๋ฅผ ์์ํ๊ณ ์๊ธฐ ๋๋ฌธ์ ๊ฐ๋ฅํ ๊ตฌ์กฐ๋ค.
๋จ ์ฐจ์ด์ ์ด ์๋ค๋ฉด PageImpl์์๋ ์ ์ฒด ๋ฐ์ดํฐ ๊ฐ์์ ๋ํ ์ ๋ณด๋ฅผ ์ถ๊ฐ๋ก ํฌํจํ๊ณ ์์ผ๋ฉฐ,
์ด์ ๋ฐ๋ผ getTotalElements(), getTotalPages()์ ๊ฐ์ ๋ฉ์๋๋ ํจ๊ป ์ ๊ณตํ๋ค.
๐ธJPA์์๋ ์ด๋ป๊ฒ Page๊ฐ์ฒด๋ฅผ ๋ฐํํ ์ ์์๊น?
YourRepository → SimpleJpaRepository → JpaQueryExecution → PageExecution → PageImpl
JPA์์ ํ์ด์ง ์์ฒญ์ ๋ด์ PageRequest๋ฅผ ์ธ์๊ฐ์ผ๋ก ๋ฐ์ Page ๊ฐ์ฒด๋ก return ํ๋ ๋ด๋ถ์ ์ธ ๋์์ ํ๋ฆ์ ์์ ๊ฐ๋ค.
1๏ธโฃ YourRepository
interface PostRepository : JpaRepository<User, Long> {
fun findByNameContaining(keyword: String, pageable: Pageable): Page<User>
}
์์ ๊ฐ์ด Page ๊ฐ์ฒด๋ฅผ ๋ฐํ๋ฐ๊ธฐ ์ํด์ ๊ฐ๋ฐ์๋ค์ Repository์ ์์ ๊ฐ์ ๋ฉ์๋๋ฅผ ์ ์ํ๋ค.
์์์ ํ์ตํ ๋ฐ๋ก๋ pageable์ ๊ฐ์ฒด๋ฅผ ํตํด Page ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ธฐ ์ํด์๋ PageImpl์ ์์ฑ์๋ฅผ ํตํด ์์ฑ๋์ด์ผ ํ๋ค.
๋ฐ๋ผ์ JPA์์๋ return์ ํ์์ด Page์ธ ๊ฒฝ์ฐ, ์์ ๊ฐ์ ์๊ทธ๋์ฒ๊ฐ ํธ์ถ๋๋ ๊ฒฝ์ฐ ๊ทธ์ ๋ง๋ SimpleJpaRepository๋ฅผ ๊ตฌํ์ฒด๋ฅผ ์ฌ์ฉํ๋ค.
2๏ธโฃ SimpleJpaRepository
SimpleJpaRepository (Spring Data JPA Parent 3.4.4 API)
java.lang.Object org.springframework.data.jpa.repository.support.SimpleJpaRepository Type Parameters: T - the type of the entity to handle ID - the type of the entity's identifier All Implemented Interfaces: JpaRepository , JpaSpecificationExecutor , JpaRe
docs.spring.io
SimpleJpaRepository ๊ณต์๋ฌธ์๋ฅผ ์ดํด๋ณด๋ฉด findAllํจ์์ spec๊ณผ pageable์ ์ธ์๋ก ํ๋ ํจ์๊ฐ ์กด์ฌํ๋ค.
๊ทธ๋ฆฌ๊ณ ํด๋น ํจ์๋ Page๋ฅผ return ํ๋ค. ๋ฐ๋ผ์ ์์์ ์ ์ํ ๋ ํฌ์งํ ๋ฆฌ ๋ฉ์๋ findByNameContaining๋
findAll(Specification<T> spec, Pageable pageable)
ํด๋น ํจ์๋ฅผ ๋ด๋ถ์ ์ผ๋ก ํธ์ถํ๋ ๊ฒ์ด๋ค.
๊ทธ๋ ๋ค๋ฉด ๋ด๋ถ ์ฝ๋ ๊ตฌ์กฐ๋ ์ด๋ ํ ํ์์ผ๊น?
spring-data-jpa/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java at main
Simplifies the development of creating a JPA-based data access layer. - spring-projects/spring-data-jpa
github.com
๋ด๋ถ์ ์ผ๋ก๋ ์์ ๊ฐ์ ๊ตฌ์กฐ๋ฅผ ๋๊ณ ์์๋ค.
TypedQuery<T> query = getQuery(spec, pageable);
spec๊ณผ pageable ์ ๋ณด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ฐ์ดํฐ ์กฐํ์ฉ JPQL ์ฟผ๋ฆฌ๋ฅผ ๋ง๋ ๋ค.
return pageable.isUnpaged()
? new PageImpl<>(query.getResultList())
: readPage(query, getDomainClass(), pageable, countSpec);
pageable.isUnpaged()์ธ ๊ฒฝ์ฐ
์ฆ, ํ์ด์ง ์ ๋ณด๊ฐ ์๋ ๊ฒฝ์ฐ ๊ทธ๋ฅ List<T>๋ง ์กฐํํด์ new PageImpl<>(...)๋ก ๊ฐ์ผ๋ค
pageable์ด ์ ํจํ ๊ฒฝ์ฐ ์ฆ, ์ค์ ํ์ด์ง๋ฅผ ์์ฒญํ๋ ๊ฒฝ์ฐ์๋ ๋ค์์ ๋ก์ง์ ๋ฐ๋ฅธ๋ค.
- ์ฟผ๋ฆฌ ์คํ (query.getResultList())
- countSpec๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์นด์ดํธ ์ฟผ๋ฆฌ ์คํ
- ๊ฒฐ๊ณผ๋ฅผ PageImpl<T>๋ก ๋ฐํ
๐งspec vs countSpec?
์กฐํ์ฉ spec๊ณผ count์ฉ spec์ ๋ฐ๋ก ๋ถ๋ฆฌํด์ ์ต์ ํ๋ ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆด ์ ์๋๋ก ํจ
=> SimpleJpaRepository์์ ์ค์ ์ฟผ๋ฆฌ ์คํ + ์นด์ดํธ ์ฟผ๋ฆฌ ์คํ + PageImpl ์์ฑ๊น์ง ์ด๋ค์ง๋ค.
3๏ธโฃ JpaQueryExecution → PageExecution → PageImpl
์ค์ ๋ฆฌํฌ์งํ ๋ฆฌ ๋ฉ์๋๊ฐ ์คํ๋ ๋๋ ์ฟผ๋ฆฌ ์คํ ์ ๋ต์ ์ ํํด์ ์คํํ๋ ๊ตฌ์กฐ์ด๋ค.
spring-data-jpa/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java at main · sp
Simplifies the development of creating a JPA-based data access layer. - spring-projects/spring-data-jpa
github.com
static class PagedExecution extends JpaQueryExecution
Repository ๋ฉ์๋์ ๋ฆฌํด ํ์ ์ด Page<T>์ผ ๊ฒฝ์ฐ, Spring Data JPA๊ฐ ์ด ์ ๋ต์ ์ ํํด์ ์คํํ๋ค.
protected Object doExecute(AbstractJpaQuery repositoryQuery, JpaParametersParameterAccessor accessor) {
Query query = repositoryQuery.createQuery(accessor);
return PageableExecutionUtils.getPage(query.getResultList(), accessor.getPageable(),
() -> count(repositoryQuery, accessor));
}
์ฐ์ JPQL ๋๋ NativeQuery๋ก ์ค์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๋ค.
๊ทธ๋ฌ๊ณ ๋์ getResultList()๋ก ๋ฐ์ดํฐ๋ฅผ ๋จผ์ ์กฐํํ ๋ค, ์นด์ดํธ ์ฟผ๋ฆฌ๋ฅผ ์คํํ ์ง ๋ง์ง๋ฅผ ํ๋จํด์ ์ต์ข ์ ์ผ๋ก PageImpl์ ๋ง๋ค์ด์ฃผ๋ ๋์ฐ๋ฏธ ํด๋์ค ์ฌ์ฉํ๋ค.
private long count(AbstractJpaQuery repositoryQuery, JpaParametersParameterAccessor accessor) {
List<?> totals = repositoryQuery.createCountQuery(accessor).getResultList();
return totals.size() == 1 ? (Long)JpaQueryExecution.CONVERSION_SERVICE.convert(totals.get(0), Long.class) : (long)totals.size();
}
๋ํ ์ด๋ PageableExecutionUtils.getPage()์ ๋ฉ์๋๋ฅผ ํธ์ถํ ๋ count()์ ๋ํ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ ๊ฒ์ ์ดํด๋ณผ ์ ์๋๋ฐ,
์ด๋, repositoryQuery.createCountQuery(...)๋ก ์นด์ดํธ ์ฟผ๋ฆฌ๋ฅผ ์์ฑ ํ ์คํํ๋ ๊ฒ์ด๋ค!
spring-data-commons/src/main/java/org/springframework/data/support/PageableExecutionUtils.java at main · spring-projects/spring
Spring Data Commons. Interfaces and code shared between the various datastore specific implementations. - spring-projects/spring-data-commons
github.com
์ด๋, ์์์ ํธ์ถํ๋ getPage์ ๋ฉ์๋๋ฅผ ๋ค์ด๊ฐ๋ณด๋ฉด ์ต์ข ์ ์ผ๋ก content, pageable, totalCount๊ฐ์ ์ธ์๋ก ๋ฐ์ PageImpl ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
JPA์์ Pageable์ ์ธ์๋ฅผ ๊ฐ์ง๊ณ Page ๊ฐ์ฒด๋ฅผ ์ด๋ป๊ฒ ์์ฑํ๋์ง์ ๋ํ ์๋ฌธ์ด ํ๋ฆฌ๋ ์๊ฐ์ด๋ค..!
์ด๋ ๊ฒ ํ์ฌ ์ต์ข ์ ์ผ๋ก JPA์์ PageImpl ๊ฐ์ฒด๋ฅผ Page ๊ฐ์ฒด๋ก ๊ฐ์ธ์ ๋ฐํํ๋ ๊ฒ์ด๋ค!
๐ธJPA์์ ์ ๊ณตํด์ฃผ๋ ๋ฐํ ํ์
Page<User> findByLastname(String lastname, Pageable pageable);
Slice<User> findByLastname(String lastname, Pageable pageable);
List<User> findByLastname(String lastname, Sort sort);
List<User> findByLastname(String lastname, Sort sort, Limit limit);
List<User> findByLastname(String lastname, Pageable pageable);
Jpa์์๋ Pagable์ ์ธ์๋ก ๋ฐ๋ ๊ฒฝ์ฐ์ Page ๊ฐ์ฒด๊ฐ ์๋์ด๋ Slice ํํ๋ ๋น์ฐํ๊ณ ,
List์ ํํ๋ก๋ ๊ฐ์ฒด๋ฅผ ๋ฐํํ ์ ์๋ค.
์ด๋ JPA๊ฐ ๋ด๋ถ์ ์ผ๋ก LIMIT, OFFSET, ORDER BY ๋ฑ์ ์๋์ผ๋ก ๋ถ์ฌ์ฃผ๋ ๋ฐฉ์ ๋๋ถ์ ๊ฐ๋ฅํ ์ ์ฐ์ฑ์ด๋ค.
๐ง ๊ทธ๋ ๋ค๋ฉด ์ด๋ค ์ํฉ์์ ์ด๋ค ํ์ ์ ์ฐ๋ ๊ฒ ์ข์๊น?
๋ฐํํ์ | ์ค๋ช | ์ ํฉํ ์ํฉ |
Page<T> | ์ ์ฒด ๊ฐ์(count(*))์ ์ ์ฒด ํ์ด์ง ์๋ฅผ ํจ๊ป ์ ๊ณต | ํ์ด์ง ๋ฒํธ์ ์ ์ฒด ํ์ด์ง ์ ํ์๊ฐ ํ์ํ ํ์ด์ง (์: ๊ฒ์ํ) |
Slice<T> | ๋ค์ ํ์ด์ง ์ ๋ฌด๋ง ํ๋จ (count(*)๋ ์คํํ์ง ์์) | ๋ฌดํ ์คํฌ๋กค, ์ฑ๋ฅ์ด ์ค์ํ ๊ฒฝ์ฐ (ex. ๋ชจ๋ฐ์ผ ํผ๋) |
List<T> | ๋จ์ํ ์ ๋ ฌ๋ ๊ฒฐ๊ณผ๋ ์ผ๋ถ ๋ฐ์ดํฐ๋ง ํ์ | ํ์ด์ง/์ ์ฒด ๊ฐ์ ๋ถํ์ํ ๋จ์ ๋ฆฌ์คํธ ์กฐํ (์: ๋๋กญ๋ค์ด ์ต์ ) |
๐จ๋์ฉ๋ ์ฟผ๋ฆฌ์ผ ๋ ์ฃผ์
ํ์ง๋ง ๊ณต์๋ฌธ์์ ๋ฐ๋ฅด๋ฉด ๋์ฉ๋ ์ฟผ๋ฆฌ ์์ฒญ์ธ ๊ฒฝ์ฐ์๋ ๊ฐ๊ฐ์ ๋ฐฉ์์ ๋ฐ๋ผ ์ฃผ์ํด์ผ ํ ์ ์ด ์กด์ฌํ๋ค.
์ฃผ์ ์ฌํญ๋ง ์ ๋ฆฌํ์๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
- List<T>: ๋ฐ์ดํฐ๊ฐ ๋ง์์๋ก ๋ฉ๋ชจ๋ฆฌ ์์ง ์ํ๊ณผ ์ ์ฒด๋ก๋ฉ์ ์๊ฐ์ด ์ค๋๊ฑธ๋ฆด ์ ์๋ค.
- Slice<T>: ์คํ์ ์ด ํฐ ๊ฒฝ์ฐ DB ์ฑ๋ฅ ์ ํ ๊ฐ๋ฅ
- Page<T>: COUNT ์ฟผ๋ฆฌ ๋น์ฉ์ด ํด ์ ์์
๋ฐ๋ผ์ ์ํฉ์ ๋ง๋ ๊ฐ์ฒด๋ก JPA์์ ๋ฐํ๋ฐ๋ ๊ฒ์ด ์ค์ํด ๋ณด์ธ๋ค!
๐ Ref.
https://hudi.blog/spring-data-jpa-pagination/#Pageable%EA%B3%BC-PageRequest