목록 페이지를 구현할 때 목록에 속한 요소의 개수가 너무 많거나, 이력 관련 페이지인 경우 모든 요소들을 한 번에 보여주지 않고 페이지를 나눠서 제공한다. JPA
에서는 한 페이지 당 표출할 데이터의 크기, 조회하고 싶은 페이지가 몇 페이지인지, 정렬 방식등을 받아 쉽게 원하는 페이지를 표출하게 할 수 있도록 Pagable
객체를 사용한다
토이 프로젝트 진행 중, 아래과 같은 화면에서 전체 제품 목록을 조회할 때, Pagable 객체를 사용하여 Pagination을 적용해 보았다
가방 너무 귀엽다👛👝
@Operation(summary = "Get All Product List")
@ApiResponse(
content = @Content(schema = @Schema(implementation = Page.class))
)
@GetMapping
public ResponseEntity<ProductRes> getProductList(
@PageableDefault(
size = 10,
page = 0,
sort = "productCode",
direction = Sort.Direction.ASC
) Pageable pageable
) {
return ResponseEntity
.status(HttpStatus.OK)
.body(productService.getProducts(pageable));
}
@PageableDefault
API 요청시 Pageable 객체에 대한 파라미터를 넘겨주지 않더라도 자동으로 기본값을 가진 Pagable 타입 파라미터를 생성
@PageableDefault
없이 API 요청 시 {
"page": 0,
"size": 1,
"sort": [
"productCode"
]
}
위와 같은 객체를 보내야 하지만, @PageableDefault
덕분에{}
만 input으로 보내도, default 설정이 반영되게 할 수 있다
@Getter
@Setter
public class ProductRes extends CommonRes {
private List<Product> content;
private SimplePageInfo pageable;
@Getter
@Setter
public static class SimplePageInfo {
private boolean last;
private int page;
private int size;
private int totalPages;
private long totalElements;
}
public ProductRes(
int status,
String message,
List<Product> content,
SimplePageInfo pageable
) {
super(status, message);
this.content = content;
this.pageable = pageable;
}
SimplePageInfo를 사용하지 않고, Page<Product>
이런 식으로 Page 객체 자체를 return하게 되면, 지나치게 많은 항목들(number, size, totalElements, totalPages, sort, first, last, ...) 이 return되게 된다. 따라서 pagination 시 필요한 변수들만 return 할 수 있도록 새로 SimplePageInfo 객체를 정의하였다
public ProductRes getProducts(Pageable pageable) {
Page<Product> page = productRepository.findAll(pageable);
List<Product> content = page.getContent();
SimplePageInfo simplePageInfo = new SimplePageInfo();
simplePageInfo.setLast(page.isLast());
simplePageInfo.setPage(page.getNumber());
simplePageInfo.setSize(page.getSize());
simplePageInfo.setTotalPages(page.getTotalPages());
simplePageInfo.setTotalElements(page.getTotalElements());
return new ProductRes(
HttpStatus.OK.value(),
"PRODUCTS ARE RETRIEVED",
content,
simplePageInfo
);
}
Page<Product> findAll(Pageable pageable);
: @PageableDefault에서 설정한 값들이 반영된다
{
"timestamp": "2023-11-18T15:06:51.446592200",
"status": 200,
"message": "PRODUCTS ARE RETRIEVED",
"content": [
{
"productCode": "product_10",
"category": {
"categoryCode": "category_15",
"parentCategory": null,
"name": "과일",
"description": "string"
},
"name": "신선한오렌지",
"price": 1300,
"stock": 10,
"img": null,
"barcode": "",
"description": ""
},
{
"productCode": "product_11",
"category": {
"categoryCode": "category_17",
"parentCategory": {
"categoryCode": "category_15",
"parentCategory": null,
"name": "과일",
"description": "string"
},
"name": "덜익은망고",
"description": "string"
},
"name": "망마라망고",
"price": 1200,
"stock": 11,
"img": null,
"barcode": "",
"description": ""
},
# ...(2 more products)
],
"pageable": {
"last": true,
"page": 0,
"size": 10,
"totalPages": 1,
"totalElements": 4
}
}
{
"page": 1,
"size": 1
}
{
"timestamp": "2023-11-18T15:10:14.826372600",
"status": 200,
"message": "PRODUCTS ARE RETRIEVED",
"content": [
{
"productCode": "product_11",
"category": {
"categoryCode": "category_17",
"parentCategory": {
"categoryCode": "category_15",
"parentCategory": null,
"name": "과일",
"description": "string"
},
"name": "덜익은망고",
"description": "string"
},
"name": "망마라망고",
"price": 1200,
"stock": 11,
"img": null,
"barcode": "",
"description": ""
}
],
"pageable": {
"last": false, # 4 페이지 중 2번째 (index 1) 페이지이므로 마지막 페이지가 아님
"page": 1,
"size": 1,
"totalPages": 4,
"totalElements": 4
}
}