쌓고 쌓다
REST API 페이징 처리 방법! 본문
JPA를 사용하여 페이징 처리된 데이터를 응답 해보자.
먼저, 다음과 같은 URL로 API를 만들고자 한다.
/locations/{locationId}/posters?page={페이지번호}&size={페이지크기}sort={정렬방법}
page, size, sort의 값은 필수가 아니며
입력하지 않은 값에대해 기본 값을 설정할 예정이다.
locationId를 포함하는 poster들을 반환해주는 API이다.
컨트롤러로 들어온 요청의 쿼리파라미터를 처리할 클래스를 다음과 같이 하나 만들어 보자.
PosterPageRequest
package com.example.spotserver.dto.request;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import lombok.Data;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
@Data
public class PosterPageRequest {
@Min(value = 1, message = "페이지는 1이상여야야 합니다.")
private int page = 1;
@Max(value = 30, message = "페이지 크기는 30을 넘을 수 없습니다.")
@Min(value = 1, message = "페이지 크기는 1이상이여야 합니다.")
private int size = 10;
private String sort = "regDate";
public void setSort(String sort) {
if(sort.equals("recent")) {
this.sort = "regDate";
} else {
this.sort = "regDate";
}
}
public org.springframework.data.domain.PageRequest makePageRequest() {
PageRequest pageRequest = null;
if(sort.equals("regDate")) {
pageRequest = org.springframework.data.domain.PageRequest.of(page-1,size, Sort.by(sort).descending());
}
return pageRequest;
}
}
- @Min, @Max : 최소 값 또는 최댓 값을 설정하여 에러 메시지를 만들기위해 작성한다.
- sort : 정렬 기준의 필드명을 갖는 String이다.
- page, size, sort : 필드 값은 기본값으로 초기화를하며 사용자가 원하는 값은 컨트롤러에서 객체로 바인딩할때 Setter로 지정된다.
- setSort : 최신순 정렬시 사용자 편의를 위해 recent를 regDate로 매핑했다.
- makePageRequest : JPA에서 페이징 처리를 위해 Pageable 인터페이스를 넘겨주면 페이징 설정에 맞춰 결과를 던져준다. Pageable의 구현체로 PageRequest가 있다.
@GetMapping("/locations/{locationId}/posters")
public ... getLocationPosters(@PathVariable Long locationId,
@Valid @ModelAttribute PosterPageRequest posterPageRequest)
{...}
위의 코드는 locationId를 포함하는 Poster 리스트를 반환하는 컨트롤러이다.
GET 요청을 보낼때 함께 넣은 쿼리 파라미터가 PosterPageRequest에 매핑된다.
쿼리 파라미터를 객체에 넣어주는 과정을 @ModelAttribute가 자동으로 해준다.
따라서 여러개의 @RequestParam을 하나의 클래스로 만들어 받으려 PosterPageRequest를 만든것이였다.
마찬가지로 API의 응답을 줄때 데이터들과 함께 페이징 정보도 함께 넣어주기위해 다음과 같은 클래스도 작성한다.
@Data
public class PageInfo {
private int page;
private int size;
private int numberOfElements;
private Long totalElements;
private int totalPage;
}
- page : 현재 페이지
- size : 한 페이지에 포함될 수 있는 요소의 최대 개수
- numberOfElements : 현재 페이지에 포함된 요소 개수
- totalElements : 전체 요소 개수
- totalPage : 전체 페이지 개수
PageResponse
@Data
public class PageResponse<T> {
private List<T> results;
private PageInfo pageInfo;
public PageResponse(Page page) {
results = page.getContent();
pageInfo = new PageInfo();
pageInfo.setPage(page.getNumber()+1); // Page의 페이지는 0부터 시작이라 사용자를 위해 +1해서 1부터 시작하게함
pageInfo.setSize(page.getSize());
pageInfo.setTotalPage(page.getTotalPages()); // 총 페이지 개수
pageInfo.setTotalElements(page.getTotalElements()); // 총 원소 개수
pageInfo.setNumberOfElements(page.getNumberOfElements()); // 현재 페이지에서 원소 개수
}
}
API의 응답으로 PageResponse를 Body에 넣어 응답할 것이다.
이때 PageResponse의 생성자로 페이징 처리의 결과인 Page를 넘겨주면
데이터와 페이징 정보를 초기화하는 과정을 거치게 했다.
PosterRepository
@Repository
public interface PosterRepository extends JpaRepository<Poster, Long> {
Page<Poster> findByLocation(Location location, Pageable pageable);
}
Spring Data Jpa의 findBy를 사용할때 마지막인자로 Pageable을 넘겨주면
Pageable의 값에 맞춰 페이징된 결과 Page<Object> 를 반환해준다.
PosterService
public PageResponse<List<PosterResponse>> getLocationPosters(Location location, Pageable pageable) {
Page<Poster> page = posterRepository.findByLocation(location, pageable);
Page<PosterResponse> dtoPage = page.map((poster) -> PosterResponse.toDto(poster));
PageResponse<List<PosterResponse>> pageResponse = new PageResponse<>(dtoPage);
return pageResponse;
}
- findByLocation : Pageable과 함께 넘겨 Page 객체를 얻는다.
- Page.map : map 함수를 사용하여 필터를 거쳐 새로운 객체를 가지도록 만들 수 있다. map을 통해 DTO로 변환해주자.
- PagePoster의 생성자로 Page를 넘겨 원하는 응답 형태인 PageResponse로 바꾼다.
PosterController
@GetMapping("/locations/{locationId}/posters")
public ResponseEntity<PageResponse<List<PosterResponse>>> getLocationPosters(@PathVariable Long locationId,
@Valid @ModelAttribute PosterPageRequest posterPageRequest) {
//요청 파라미터로 들어온 이름, 나이를 객체를 생성하여 값을 넣어주는 과정을 거친다.
//위의 과정을 스프링 @ModelAttribute로 자동화 할 수 있다.
PageRequest pageRequest = posterPageRequest.makePageRequest();
Location location = locationService.getLocation(locationId);
PageResponse<List<PosterResponse>> posters = posterService.getLocationPosters(location, pageRequest);
return ResponseEntity
.status(HttpStatus.OK)
.body(posters);
}
이제 컨트롤러 단에서 응답을 보내는 과정이 보일 것이다.
PosterPageRequest를 통해 페이징 설정 정보를 받고 PageRequest로 변환한다.
이 PageRequest를 가지고 JPA에게 페이징 요청을 한다.
그 결과를 자신에게 맞게 응답을 만들어 보내면 된다!
구현 결과!!
'프로그래밍 > spring' 카테고리의 다른 글
Controller 테스트시 401, 403 에러 발생 (WebMvcTest에 SecurityConfig 설정하는법) (1) | 2024.01.31 |
---|---|
Mockito given willReturn이 동작하지 않을때 (0) | 2024.01.30 |
Spring Security 404 에러 대신 401 에러 뜰때 (1) | 2024.01.28 |
REST API 테스트 코드 작성하기 with MockMVC (0) | 2024.01.27 |
@BeforeAll, @AfterAll non-static 메서드로 호출하는법 (0) | 2024.01.26 |