1. Page 응답을 커스텀한 이유
기본적으로 Spring Data JPA는 페이지네이션된 데이터를 반환할 때 Page<T> 객체를 제공합니다.
하지만 Page<T> 객체는 많은 메타데이터를 포함하고 있어 클라이언트가 필요로 하지 않는 정보까지 응답에 포함됩니다. 클라이언트가 필요한 정보만 가볍게 전달하기 위해 PageResponse라는 커스텀 DTO를 만들어 원하는 정보만 담도록 직렬화해서 사용하였습니다.
주요 목적
- 응답 구조 간소화: 필요한 데이터와 페이지 메타데이터만 포함하여 클라이언트가 쉽게 이해하고 파싱할 수 있도록 만듭니다.
- 불필요한 데이터 제거: 응답 크기를 줄이고 성능을 개선합니다.
- 일관성 있는 응답 제공: 모든 페이지 응답이 동일한 구조를 가짐으로써, 클라이언트는 일관된 API 경험을 얻게 됩니다.
2. PageResponse DTO 구현
페이지 응답을 간단한 JSON 형식으로 제공하기 위한 DTO를 만들기 위해 PageResponse Record 클래스를 생성 해주었습니다.
from() 메서드를 통해 Page<T> 객체를 PageResponse로 변환할 수 있습니다.
@Schema(description = "커스텀된 페이지 응답 DTO")
public record PageResponse<E>(
boolean hasNext,
List<E> items,
int pageNumber,
int pageSize,
int totalPages,
long totalElements,
boolean isLast
) {
public static <E> PageResponse<E> from(final Page<E> page) {
return new PageResponse<>(
page.hasNext(),
page.getContent(),
page.getNumber(),
page.getSize(),
page.getTotalPages(),
page.getTotalElements(),
page.isLast()
);
}
}
3. CustomPageSerializer 구현
이제 PageResponse를 활용하려면 CustomPageSerializer가 필요합니다.
해당 클래스는 Page<T> 객체를 받아 PageResponse 형태로 JSON 응답을 직렬화합니다.
@JsonComponent
public class CustomPageSerializer<T> extends JsonSerializer<Page<T>> {
@Override
public void serialize(Page<T> page, JsonGenerator gen, SerializerProvider serializers) throws IOException {
PageResponse<T> response = PageResponse.from(page);
gen.writeObject(response);
}
}
@JsonComponent를 통해 CustomPageSerializer를 등록하면 Spring Boot가 자동으로 페이지 응답을 PageResponse 형식으로 직렬화하게 됩니다.
4. 적용 결과
{
"hasNext": false,
"items": [
{
"id": 73,
"name": "Product1",
"price": {
"price": 1000
},
"quantity": 50,
"openTime": "2024-10-16T09:00:00"
},
{
"id": 74,
"name": "Product2",
"price": {
"price": 5500
},
"quantity": 100,
"openTime": "2024-10-16T09:00:00"
},
...
],
"pageNumber": 0,
"pageSize": 20,
"totalPages": 1,
"totalElements": 8,
"isLast": true
}
이렇게 CustomPageSerializer와 PageResponse를 통해 클라이언트가 쉽게 이해하고 사용할 수 있는 응답 구조가 되었습니다.