쌓고 쌓다

REST API 페이징 처리 방법! 본문

프로그래밍/spring

REST API 페이징 처리 방법!

승민아 2024. 1. 29. 00:13

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에게 페이징 요청을 한다.

그 결과를 자신에게 맞게 응답을 만들어 보내면 된다!

 

 

구현 결과!!

Comments