본문 바로가기

Programming/Spring Boot

[Spring Boot] JPA + Pageable 을 이용한 페이징 처리

반응형
안녕하세요. 남산돈가스 입니다.

오늘은 Spring Boot JPA를 이용하여 API 개발 시 간단하게 Pagination 와 Sorting을 처리할 수 있도록 도와주는 Pageable에 대해서 알아보려고 합니다.

웹 개발 시 Pagination 과 Sorting은 필수적이라 할 수 있지만, 실제 개별적으로 구현 시 번거로운 작업이 생기기 마련입니다. 또한 각 데이터베이스마다 페이징 쿼리가 다를 수 있다는 점에서 모든 요구조건을 만족하기 어려운 부분들이 존재하기 마련입니다.

Spring Data JPA와 Pageable을 이용하면 이런 문제들을 아주 쉽게 해결할 수 있어 비즈니스 로직에 집중할 수 있게 도와줍니다.

우선 Pageable을 사용하여 얻을 수 있는 이점은 대표적으로 두 가지 입니다.
  1. 요건에 맞는 Pagination을 구현할 수 있다.
  2. 정렬이 필요한 데이터를 쉽게 Sorting 할 수 있다.
예제를 들어 설명하겠습니다.

@Entity
@Table(name="post")
@Getter
public class Post extends Audity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="id")
private Long id;

@Column(name="title")
private String title;

@Column(length = 2000, name="content")
private String content;

@Column(length = 50, name="writer")
private String writer;
}

간단한 예제를 들기 위하여, "Post" 라는 Entity를 작성하였고, 예제를 위하여 약 20건의 데이터를 생성해두었습니다.

@Repository
public interface PostRepository extends JpaRepository<Post, Long> {
}

생성한 Post Entity를 접근하기 위하여 JpaRepository를 상속받는 PostRepository를 생성합니다.


위 다이어그램에서와 같이 생성한 PostRepository는 JpaRepository를 상속받았고, 또 JpaRepository는 PagingAndSortingRepository를 상속받은 것을 확인할 수 있습니다.
이렇게 PagingAndSortingRepository만 상속받은 Repository를 생성한다면, Pageable을 매개변수로 Pagination / Sorting 을 처리할 준비가 끝난 것 입니다.

@RestController
@RequestMapping("/posts")
public class PostController {

@Autowired
private PostRepository postRepository;

@GetMapping
public ResponseEntity retrievePosts(final Pageable pageable) {
Page<Post> posts = postRepository.findAll(pageable);
return new ResponseEntity<>(posts,HttpStatus.OK);
}
}

그럼 이제, 외부에서 Post를 조회할 수 있도록 Controller를 생성합니다.
이 예제에서는 블로그 목적에 맞게 Pageable을 설명하기 위함이므로 Controller -> Repository 를 호출하는 구조로 작성하였습니다. 실제 구현 시에는 Service와 목적에 맞는 DTO를 생성하여 처리하는 것이 안전합니다.
- 파라미터 정보 없이 요청한 경우




{
"content": [
{
"createdDate": "2020-03-13T07:05:46",
"updatedDate": "2020-03-13T07:05:46",
"id": 1,
"title": "1번 게시글",
"content": "안녕하세요",
"writer": "남산돈가스"
},
{
"createdDate": "2020-03-13T08:05:46",
"updatedDate": "2020-03-13T07:05:46",
"id": 2,
"title": "2번 게시글",
"content": "안녕하세요",
"writer": "남산돈가스"
},
{
"createdDate": "2020-03-13T09:05:46",
"updatedDate": "2020-03-13T07:05:46",
"id": 3,
"title": "3번 게시글",
"content": "안녕하세요",
"writer": "명동교자\n"
},
{
"createdDate": "2020-03-13T10:05:46",
"updatedDate": "2020-03-13T07:05:46",
"id": 4,
"title": "4번 게시글",
"content": "안녕하세요",
"writer": "남산돈가스"
},
{
"createdDate": "2020-03-13T11:05:46",
"updatedDate": "2020-03-13T07:05:46",
"id": 5,
"title": "5번 게시글",
"content": "안녕하세요",
"writer": "남산타워"
},
{
"createdDate": "2020-03-13T12:05:46",
"updatedDate": "2020-03-13T07:05:46",
"id": 6,
"title": "6번 게시글",
"content": "안녕하세요",
"writer": "남산돈가스"
},
{
"createdDate": "2020-03-13T13:05:46",
"updatedDate": "2020-03-13T07:05:46",
"id": 7,
"title": "7번 게시글",
"content": "안녕하세요",
"writer": "남산돈가스"
},
{
"createdDate": "2020-03-13T14:05:46",
"updatedDate": "2020-03-13T07:05:46",
"id": 8,
"title": "8번 게시글",
"content": "안녕하세요",
"writer": "명동교자\n"
},
{
"createdDate": "2020-03-13T15:05:46",
"updatedDate": "2020-03-13T07:05:46",
"id": 9,
"title": "9번 게시글",
"content": "안녕하세요",
"writer": "남산돈가스"
},
{
"createdDate": "2020-03-13T16:05:46",
"updatedDate": "2020-03-13T07:05:46",
"id": 10,
"title": "10번 게시글",
"content": "안녕하세요",
"writer": "남산돈가스"
}
],
"pageable": {
"sort": {
"sorted": false,
"unsorted": true,
"empty": true
},
"offset": 0,
"pageSize": 10,
"pageNumber": 0,
"paged": true,
"unpaged": false
},
"totalElements": 18,
"last": false, // 마지막 페이지 여부
"totalPages": 2, // 전체 페이지 갯수 총 18건 데이터 중 10개의 요청이므로 2개 페이지 존재
"size": 10, // 페이지 당 출력 갯수
"number": 0,
"numberOfElements": 10, // 요청 페이지에서 조회 된 데이터의 갯수
"sort": {
"sorted": false,
"unsorted": true,
"empty": true
},
"first": true, // 첫 페이지 여부
"empty": false
}

위 와 같이 contents 에는 실제 DB로부터 요청한 결과가 보여지며, 그 이외는 Paging 결과를 확인할 수 있는 값들이 다양하게 표현되어 있습니다. 이 값들을 가지고 공통적으로 처리되는 페이징 결과를 만들어 처리할 수 있을 것 같습니다.

그럼 이제 실제 요청 값에 페이징 정보를 전달해보겠습니다.
Controller의 매개변수인 Pageable은 Spring MVC가 요청파라미터로부터 기본 설정 된 파라미터 값이 존재하면 Pageable 객체를 생성하려고 시도합니다. 이때 기본 설정 된 파라미터는 아래와 같습니다.
  • page : 요청할 페이지 번호
  • size : 한 페이지 당 조회 할 갯수 (default : 20)
  • sort : Sorting에 대한 값 설정하는 파라미터로, 기본적으로 오름차순이다. 표기는 정렬한 필드명,정렬기준 ex) createdDate,desc
제가 사용한 예제에서는 default size를 10, sort 파라미터를 sortBy, 구분자를 '-' 로 커스터마이징하여서 사용하였습니다. pageable 를 커스터마이징하는 방법은 추후에 포스팅하도록 하겠습니다.  
- size : 3 , page = 2 로 호출한 경우

{
"content": [
{
"createdDate": "2020-03-13T10:05:46",
"updatedDate": "2020-03-13T07:05:46",
"id": 4,
"title": "4번 게시글",
"content": "안녕하세요",
"writer": "남산돈가스"
},
{
"createdDate": "2020-03-13T11:05:46",
"updatedDate": "2020-03-13T07:05:46",
"id": 5,
"title": "5번 게시글",
"content": "안녕하세요",
"writer": "남산타워"
},
{
"createdDate": "2020-03-13T12:05:46",
"updatedDate": "2020-03-13T07:05:46",
"id": 6,
"title": "6번 게시글",
"content": "안녕하세요",
"writer": "남산돈가스"
}
],
"pageable": {
"sort": {
"sorted": false,
"unsorted": true,
"empty": true
},
"offset": 3,
"pageSize": 3,
"pageNumber": 1,
"paged": true,
"unpaged": false
},
"totalElements": 18,
"last": false,
"totalPages": 6,
"size": 3,
"number": 1,
"numberOfElements": 3,
"sort": {
"sorted": false,
"unsorted": true,
"empty": true
},
"first": false,
"empty": false
}


- size : 5, page :4, sorting : 생성일자 기준 내림차순 정렬


{
"content": [
{
"createdDate": "2020-03-13T09:05:46",
"updatedDate": "2020-03-13T07:05:46",
"id": 3,
"title": "3번 게시글",
"content": "안녕하세요",
"writer": "명동교자\n"
},
{
"createdDate": "2020-03-13T08:05:46",
"updatedDate": "2020-03-13T07:05:46",
"id": 2,
"title": "2번 게시글",
"content": "안녕하세요",
"writer": "남산돈가스"
},
{
"createdDate": "2020-03-13T07:05:46",
"updatedDate": "2020-03-13T07:05:46",
"id": 1,
"title": "1번 게시글",
"content": "안녕하세요",
"writer": "남산돈가스"
}
],
"pageable": {
"sort": {
"sorted": true,
"unsorted": false,
"empty": false
},
"offset": 15,
"pageSize": 5,
"pageNumber": 3,
"paged": true,
"unpaged": false
},
"totalElements": 18,
"last": true,
"totalPages": 4,
"size": 5,
"number": 3,
"numberOfElements": 3,
"sort": {
"sorted": true,
"unsorted": false,
"empty": false
},
"first": false,
"empty": false
}

총 18개 데이터에서 5개씩 4번째 페이지를 요청한 결과 마지막 페이지의 3개의 데이터가 조회되었으며, 정렬조건으로 생성일자 내림차순으로 요청하여 최신으로 생성 된 세개의 결과가 조회되었습니다.

결론적으로, 이렇게 Spring Data JPA와 Pageable을 유연하게 사용하시면 매번 Pagination 이나 Sorting에 대한 부담 및 고민을 쉽게 해결하실 수 있으실 것이라고 생각됩니다.

감사합니다.