[TIL] Spring Boot Pageable, PageableDefault 어노테이션
2025-04-22 TIL
📝 TIL (Today I Learned)
🔗 원본 이슈: #46
📅 작성일: 2025-04-22
🔄 최종 수정: 2025년 05월 02일
🍀 새롭게 배운 것
Spring Boot Paging
프로젝트에서 커뮤니티 부분을 구현하며 게시글 목록을 조회할 때, 어떤 식으로 반환해야하는지 찾아보다 Pagination을 알게 되었다.
Spring JPA 라이브러리의 Pageable을 이용하는 방법을 사용했다.
- Pageable
- Spring JPA에서 DB 쿼리에 쉽고 유연하게 limit쿼리를 사용할 수 있게 해준다.
- 특히 JPA를 사용할 때, 자동으로 Pageable 타입의 변수를 넘겨주면 JPA가 DB에 접근해 데이터를 가져올 때 자동으로 limit조건을 붙여 데이터를 가져온다.
- Pageable
PageableDefault 어노테이션의 역할
@PageableDefault(size = 10)
어노테이션은 다음과 같은 역할을 합니다:
기본 페이지 크기 설정: 클라이언트가 별도의 페이지 크기(
size
파라미터)를 지정하지 않았을 때 기본적으로 페이지당 10개의 항목을 반환하도록 설정합니다.기본값 제공: 클라이언트가 페이징 관련 파라미터를 명시적으로 지정하지 않았을 때 사용할 기본값을 제공합니다.
컨트롤러 메서드 간소화: 매번 페이징 관련 파라미터를 검증하고 기본값을 설정하는 코드를 작성하지 않아도 됩니다.
PageableDefault의 주요 속성
@PageableDefault
는 다음과 같은 속성을 지원합니다:
@PageableDefault(
size = 10, // 페이지당 항목 수 (기본값: 10)
page = 0, // 시작 페이지 번호 (기본값: 0, 첫 페이지는 0부터 시작)
sort = {}, // 정렬 기준 필드명
direction = Sort.Direction.ASC // 정렬 방향 (기본값: 오름차순)
)
Pageable 관련 알아야 할 개념들
1. Pageable 인터페이스
Pageable
은 페이징 정보를 캡슐화하는 Spring Data의 인터페이스입니다. 주요 메서드는 다음과 같습니다:
getPageNumber()
: 현재 페이지 번호 (0부터 시작)getPageSize()
: 페이지 크기getSort()
: 정렬 정보getOffset()
: 전체 결과에서의 오프셋 (첫 항목의 위치)next()
,previous()
,first()
: 다음/이전/첫 페이지의 Pageable 객체 생성
2. Page 인터페이스
Page<T>
는 페이징된 결과를 담는 인터페이스로, 다음과 같은 메서드를 제공합니다:
getContent()
: 현재 페이지의 내용(리스트)getTotalElements()
: 전체 항목 수getTotalPages()
: 전체 페이지 수getNumber()
: 현재 페이지 번호 (0부터 시작)getSize()
: 페이지 크기hasNext()
,hasPrevious()
: 다음/이전 페이지 존재 여부
3. 요청 파라미터
클라이언트는 다음과 같은 요청 파라미터를 통해 페이징 정보를 전달할 수 있습니다:
page
: 페이지 번호 (0부터 시작)size
: 페이지 크기sort
: 정렬 기준과 방향 (예:sort=createdAt,desc
)
4. Sort 클래스
Sort
는 정렬 정보를 나타내는 클래스로, 다음과 같은 방식으로 사용됩니다:
Sort.by(Direction.DESC, "createdAt")
여러 필드로 정렬할 때:
Sort.by(
Sort.Order.desc("createdAt"),
Sort.Order.asc("title")
)
5. PageRequest 클래스
PageRequest
는 Pageable
의 구현체로, 다음과 같이 생성할 수 있습니다:
PageRequest.of(0, 10, Sort.by(Direction.DESC, "createdAt"))
6. JpaRepository에서의 사용
JpaRepository
는 페이징을 지원하는 메서드를 제공합니다:
Page<Entity> findAll(Pageable pageable);
Page<Entity> findByField(String value, Pageable pageable);
코드에서 보이는 findAllByOrderByCreatedAtDesc(Pageable pageable)
와 같은 메서드는 이러한 패턴을 따르고 있습니다.
7. 커스텀 쿼리와 페이징
@Query
어노테이션으로 작성한 JPQL 쿼리에서도 Pageable
파라미터를 사용할 수 있습니다:
@Query("SELECT p FROM Post p WHERE p.title LIKE %:keyword%")
Page<Post> searchByTitle(@Param("keyword") String keyword, Pageable pageable);
실제 동작 방식
제공된 코드에서 getPosts
메서드는 다음과 같이 동작합니다:
클라이언트가
/posts?page=1&size=20&sort=viewCount,desc
와 같은 요청을 보내면, Spring은 이 파라미터를Pageable
객체로 변환합니다.만약 클라이언트가
size
파라미터를 지정하지 않았다면,@PageableDefault(size = 10)
에 따라 기본값 10이 사용됩니다.PostService
의getPosts
메서드에 이Pageable
객체가 전달되고, 이것은 다시PostRepository
의 적절한 메서드로 전달됩니다.데이터베이스 쿼리는
LIMIT
와OFFSET
절을 포함하여 페이징을 구현합니다. 예를 들어,page=1&size=10
이라면 SQL에서는LIMIT 10 OFFSET 10
과 같이 변환됩니다.결과는
Page<PostDto.ListResponse>
객체로 반환되며, 이 객체는 현재 페이지의 내용뿐만 아니라 전체 페이지 수, 전체 항목 수 등의 메타데이터도 포함합니다.
예제 응답 형태
API 응답은 다음과 같은 JSON 형태로 반환됩니다:
{
"content": [
{ "id": 1, "title": "게시글 1", ... },
{ "id": 2, "title": "게시글 2", ... },
...
],
"pageable": {
"sort": {
"empty": false,
"sorted": true,
"unsorted": false
},
"offset": 10,
"pageNumber": 1,
"pageSize": 10,
"paged": true,
"unpaged": false
},
"last": false,
"totalElements": 42,
"totalPages": 5,
"size": 10,
"number": 1,
"sort": {
"empty": false,
"sorted": true,
"unsorted": false
},
"first": false,
"numberOfElements": 10,
"empty": false
}
최적화 고려사항
카운트 쿼리 최적화: 항목이 많은 경우
totalElements
를 계산하기 위한 COUNT 쿼리가 성능에 영향을 줄 수 있습니다. 필요에 따라countQuery
최적화를 고려해야 합니다.인덱스 활용: 정렬 및 필터링에 사용되는 필드에 적절한 인덱스를 생성해야 합니다.
페이지 사이즈 제한: 클라이언트가 지나치게 큰 페이지 크기를 요청하지 못하도록 제한할 수 있습니다.
Pageable 관련 추가 정보
Pageable
파라미터를 사용하는 메서드는 자동으로spring-data-commons
의PageableHandlerMethodArgumentResolver
에 의해 처리됩니다.애플리케이션 전체에서 페이징 기본값을 변경하려면
application.properties
또는application.yml
에서 설정할 수 있습니다:
spring:
data:
web:
pageable:
default-page-size: 20
max-page-size: 100
one-indexed-parameters: true # 페이지 번호를 1부터 시작하도록 설정
- RESTful API에서 페이징 정보를 전달하는 다른 방법으로는 헤더 방식, 경로 변수 방식 등이 있지만, Spring Data는 기본적으로 쿼리 파라미터 방식을 사용합니다.