ํ์ด์ง๋ค์ด์ ๋ฐฉ์ ์ ํ ๊ณผ์
๋ชฉ์ฐจ
1. ๋ฐฐ๊ฒฝ
ํ์ด์ง๋ค์ด์ ์ด๋?
ํ์ด์ง๋ค์ด์ (Page Pagination)์ ๋ง์ ๋ฐ์ดํฐ๋ฅผ ํ ๋ฒ์ ์กฐํํ์ง ์๊ณ , ์ผ์ ๊ฐ์์ฉ ๋๋์ด ์กฐํํ๋ ๋ฐฉ์์ด๋ค. ๋ฐ์ดํฐ ์์ด ๋ง์์ง์๋ก ํ ๋ฒ์ ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๊ฒ์ ์ฑ๋ฅ ์ ํ๋ก ์ด์ด์ง ์ ์๊ธฐ ๋๋ฌธ์, ์ฌ์ฉ์์๊ฒ๋ ํ์ํ ๋งํผ๋ง ๋ฐ์ดํฐ๋ฅผ ๋ณด์ฌ์ฃผ๊ณ ์๋ฒ ๋ถํ๋ฅผ ์ค์ด๊ธฐ ์ํด ์ฌ์ฉ๋๋ค.

๊ฒ์๋ฌผ, ๋ฉ๋ชจ, ์ถ์ฒ ๋์ ๋ชฉ๋ก์ด ์ ์ฒด ์กฐํ ๋ฐฉ์์ผ๋ก ๊ตฌํ๋์ด ์์๋ค. ์ด๊ธฐ ๋ฐ์ดํฐ๊ฐ ์ ์ ๋๋ ๋ฌธ์ ๊ฐ ์์ง๋ง, ๋ฐ์ดํฐ๊ฐ ๊ณ์ ์์ด๋ฉด ํ ๋ฒ์ ๋ง์ ๋ฐ์ดํฐ๋ฅผ ์กฐํํ๊ฒ ๋์ด ์๋ต ์๋์ ์๋ฒ ๋ถํ ์ธก๋ฉด์์ ๋ฌธ์ ๊ฐ ์๊ธธ ์ ์๋ค.
๊ทธ๋์ ๋ชฉ๋ก ์กฐํ API์ ํ์ด์ง๋ค์ด์
์ ๋์
ํ๊ธฐ๋ก ํ๊ณ , ๊ทธ ๊ณผ์ ์์ ์คํ์
๊ธฐ๋ฐ ํ์ด์ง๋ค์ด์
๊ณผ ์ปค์ ๊ธฐ๋ฐ ํ์ด์ง๋ค์ด์
์ค ์ด๋ค ๋ฐฉ์์ ์ฌ์ฉํ ์ง ๋น๊ตํ๋ค.
2. ๋ฐฉ์ ๋น๊ต
์คํ์ ๊ธฐ๋ฐ ํ์ด์ง๋ค์ด์
์คํ์
๊ธฐ๋ฐ ํ์ด์ง๋ค์ด์
์ Spring Data์ Pageable์ ํ์ฉํ๋ ๋ฐฉ์์ด๋ค. ๋ฐ์ดํฐ๋ฒ ์ด์ค์์๋ LIMIT๊ณผ OFFSET์ ์ฌ์ฉํด ํน์ ํ์ด์ง์ ๋ฐ์ดํฐ๋ฅผ ์กฐํํ๋ค.


Page<Post>๋ฅผ ๋ฐํ ํ์ ์ผ๋ก ์ง์ ํ๋ฉด Spring Data JPA๊ฐ ์๋์ผ๋ก count ์ฟผ๋ฆฌ๋ฅผ ํจ๊ป ์คํํด ์ ์ฒด ํ์ด์ง ์๋ฅผ ๊ณ์ฐํ๋ค. Pageable์ ์ปจํธ๋กค๋ฌ์์ ์ ๋ฌ๋ฐ์ page, size ๊ฐ์ ๋ด๋ ๊ฐ์ฒด๋ค.
Repository์์ ๋ฐํ๋ Page<Post>๋ฅผ map()์ผ๋ก DTO๋ก ๋ณํํ๋ค. ์ํฐํฐ๊ฐ ์ธ๋ถ๋ก ๋ ธ์ถ๋์ง ์๋๋ก PostResponse::from ์ ์ ํฉํ ๋ฆฌ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ค.


page์ size์ ๊ธฐ๋ณธ๊ฐ์ ์ค์ ํด ํ๋ผ๋ฏธํฐ ์์ด ์์ฒญํด๋ ๋์ํ๋๋ก ํ๋ค. page=0์ ์ฒซ ๋ฒ์งธ ํ์ด์ง, size=10์ ํ ํ์ด์ง์ 10๊ฐ๋ฅผ ์๋ฏธํ๋ค.
| ์ฅ์ | ๋จ์ |
|---|---|
| Spring Data์์ ๊ธฐ๋ณธ ์ง์ํ๋ฏ๋ก ๊ตฌํ์ด ๋จ์ํ๋ค. | ํ์ด์ง๊ฐ ๋ค๋ก ๊ฐ์๋ก OFFSET N๋งํผ ๊ฑด๋๋ฐ๋ ๋น์ฉ์ด ์ปค์ง๋ค. |
| ํ์ด์ง ๋ฒํธ๋ฅผ ๊ธฐ์ค์ผ๋ก ์ํ๋ ํ์ด์ง์ ๋ฐ๋ก ์ ๊ทผํ ์ ์๋ค. | ๋ฐ์ดํฐ ์ถ๊ฐ ๋๋ ์ญ์ ๊ฐ ๋ฐ์ํ๋ฉด ์ค๋ณต์ด๋ ๋๋ฝ์ด ์๊ธธ ์ ์๋ค. |
| ์ ์ฒด ํ์ด์ง ์์ ์ ์ฒด ๋ฐ์ดํฐ ๊ฐ์๋ฅผ ์ ๊ณตํ ์ ์๋ค. | ๋๊ท๋ชจ ๋ฐ์ดํฐ์์๋ ์ฑ๋ฅ ์ ํ๊ฐ ๋ฐ์ํ ์ ์๋ค. |
์ปค์ ๊ธฐ๋ฐ ํ์ด์ง๋ค์ด์
์ปค์ ๊ธฐ๋ฐ ํ์ด์ง๋ค์ด์ ์ ๋ง์ง๋ง์ผ๋ก ์กฐํํ ํญ๋ชฉ์ ์๋ณ์๋ฅผ ์ปค์๋ก ์ผ์, ๊ทธ ์ดํ ๋๋ ์ด์ ๋ฐ์ดํฐ๋ฅผ ์กฐํํ๋ ๋ฐฉ์์ด๋ค.
// Repository
List<Post> findByUserIdAndIdLessThanOrderByIdDesc(Long userId, Long cursor, Pageable pageable);
// Service
public List<PostResponse> getPosts(Long userId, Long cursor, int size) {
Pageable pageable = PageRequest.of(0, size);
return postRepository.findByUserIdAndIdLessThanOrderByIdDesc(userId, cursor, pageable)
.stream().map(PostResponse::from).toList();
}โ
์ปค์ ๊ธฐ๋ฐ ํ์ด์ง๋ค์ด์ ๊ตฌํ ์์
| ์ฅ์ | ๋จ์ |
|---|---|
| ์ผ์ ํ ์ฑ๋ฅ ์ ์ง | ์ง์ ๊ตฌํ ํ์, ๋ณต์ก๋ ๋์ |
| ์ค๋ณต/๋๋ฝ ์์ | ์ ์ฒด ํ์ด์ง ์ ์ ๊ณต ์ด๋ ค์ |
| ๋ฌดํ ์คํฌ๋กค์ ์ ํฉ | ์์ ํ์ด์ง ์ ๊ทผ ๋ถ๊ฐ |
3. ๊ฒฐ์
Booktine์์๋ ์คํ์ ๊ธฐ๋ฐ ํ์ด์ง๋ค์ด์ ์ ์ ํํ๋ค.
ํ์ฌ ์๋น์ค ๊ท๋ชจ์์๋ ์คํ์
๊ธฐ๋ฐ์ ์ฑ๋ฅ ๋ฌธ์ ๋ณด๋ค, Spring Data์ Pageable์ ๊ทธ๋๋ก ์ฌ์ฉํ ์ ์๋ ๊ตฌํ ๋จ์์ฑ์ด ๋ ํฐ ์ฅ์ ์ด๋ผ๊ณ ํ๋จํ๋ค.
PageRequest ๊ธฐ๋ณธ๊ฐ ์ค์ ์ด์
PageRequest.of(page, size)์ ๊ธฐ๋ณธ๊ฐ์ page=0, size=10์ผ๋ก ์ค์ ํ๋ค. ์ฒซ ํ์ด์ง๋ถํฐ 10๊ฐ์ฉ ๋
ธ์ถํ๋ ๋ฐฉ์์ด ๋
์ ๊ธฐ๋ก ๋ชฉ๋ก UI์ ์ ํฉํ๋ค๊ณ ํ๋จํ๊ธฐ ๋๋ฌธ์ด๋ค.
Page<T> ์๋ต ๊ตฌ์กฐ
{
"content": [
{
"id": 1,
"title": "๋
์ ๊ธฐ๋ก ์ ๋ชฉ",
"content": "๋
์ ๋ฉ๋ชจ ๋ด์ฉ"
}
],
"totalPages": 3,
"totalElements": 25,
"number": 0,
"size": 10
}
4. Page vs Slice ์ ํ ๊ณผ์
// Slice<T> — count ์ฟผ๋ฆฌ ์์, ๋ค์ ํ์ด์ง ์กด์ฌ ์ฌ๋ถ๋ง ๋ฐํ (๋ฌดํ ์คํฌ๋กค)
Slice<Post> findAllByUserId(Long userId, Pageable pageable);
// Page<T> — count ์ฟผ๋ฆฌ ๋ฐ์, ์ ์ฒด ํ์ด์ง ์/ํ์ฌ ํ์ด์ง ์ ๋ณด ๋ฐํ (ํ์ด์ง ๋ฒํธ UI)
Page<Post> findAllByUserId(Long userId, Pageable pageable);
๋ ๋ฐฉ์์ ์ฐจ์ด๋ count ์ฟผ๋ฆฌ ์ ๋ฌด๋ค. Slice<T>๋ ๋ค์ ํ์ด์ง ์กด์ฌ ์ฌ๋ถ(hasNext())๋ง ๋ฐํํ๊ณ , Page<T>๋ ์ ์ฒด ํ์ด์ง ์์ ์ ์ฒด ๋ฐ์ดํฐ ๊ฐ์๊น์ง ๋ฐํํ๋ค.
Slice<T>๋ count ์ฟผ๋ฆฌ๊ฐ ์์ด์ ์ฑ๋ฅ์ ์ ๋ฆฌํ์ง๋ง, ๋ฌดํ ์คํฌ๋กค UI์ ์ ํฉํ ๋ฐฉ์์ด๋ค.
๋ฐ๋ฉด ์ด ํ๋ก์ ํธ๋ ํ์ด์ง ๋ฒํธ ๊ธฐ๋ฐ UI๊ฐ ๋ ์ ํฉํ๋ค๊ณ ํ๋จํด ์ ์ฒด ํ์ด์ง ์ ๋ณด๋ฅผ ์ ๊ณตํ๋ Page<T>๋ฅผ ์ ํํ๋ค.
'๐ป PROJECT > [Spring Boot, React] ๋ ์ ์ต๊ด ๊ด๋ฆฌ ์๋น์ค' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| [ํธ๋ฌ๋ธ์ํ ] JavaMailSender ๋น ๋ฑ๋ก ์ค๋ฅ (๋ก์ปฌ ํ๊ฒฝ) (0) | 2026.05.03 |
|---|---|
| [ํ๋ก์ ํธ] ๋ฆฌ๋ง์ธ๋ ์๋ฆผ ๊ตฌํ ๋ฐฉ์ ์ ํ ๊ณผ์ (0) | 2026.05.01 |
| [ํ๋ก์ ํธ] ๊ณตํต ์๋ต ํฌ๋งท ์ค๊ณ (0) | 2026.04.30 |
| [ํ๋ก์ ํธ] ์ด๋ฏธ์ง ์ ๋ก๋ ์ ์ฑ ๊ฒฐ์ ๊ณผ์ (0) | 2026.04.30 |
| [ํธ๋ฌ๋ธ์ํ ] Spring Boot์์ ๋์ ์ฟผ๋ฆฌ ๊ฐ์ ํ๊ธฐ: @Query์์ QueryDSL๋ก (0) | 2026.04.29 |