[SpringBoot]JPA, Page, Pageable, PageRequest / Paging 구현

doooly·2024년 2월 21일
0

SpringBoot

목록 보기
2/3

📄 Paging이란?

게시판 기능을 사용하다보면, 리스트 하단에 아래 사진처럼 페이지 번호들이 나열되어있습니다. 뿐만 아니라 많은 양의 데이터를 조회할 때도 아래처럼 페이징 기능이 사용되죠. 페이징은 이처럼 한 번에 뱉는 데이터 양을 조절하며, 서버 부하를 막고 가독성을 높이기 위해 사용됩니다


🔗 interface

Pageable

페이징 처리를 위한 정보들을 담기 위해 사용하는 인터페이스입니다
페이지 번호, 크기, offset 등의 정보들을 취할 수 있습니다

PageRequest

pageable 인스턴스에 정보를 담기 위해 사용되는 클래스입니다
of 메소드를 사용해 해당 인스턴스를 생성할 수 있습니다
페이지 번호와 페이지 크기, 정렬 기준 등을 인자로 사용합니다


✅ Practice

프로젝트를 진행하며 포트폴리오 전체 조회, 포트폴리오 검색, 아티스트 조회 등의 기능을 위해 페이징 처리를 구현했으며, 아래의 예제는 페이징 처리에 초점을 맞추기 위해 포트폴리오 전체 조회 API 에 대한 코드를 사용했습니다

1️⃣ Controller

artistId를 받아 해당 아티스트의 포트폴리오를 조회하는 API입니다
@RequestParam을 통해 page 번호를 받아 service에 넘겨줍니다

 @Operation(summary = "포트폴리오 전체 조회", description = "포트폴리오 전체를 조회하는 API입니다.")
    @GetMapping("/{artistId}")
    public ApiResponse getPortfolio(@PathVariable Long artistId,
                                    @RequestParam(value = "page", defaultValue = "0", required = false) int page
                                    ){
        return ApiResponse.SuccessResponse(SuccessStatus.PORTFOLIO_GET, portfolioService.getPortfolio(artistId, page));
    }

PageableDefault를 사용해 정렬과 페이징 관련 인자들을 받는 방식도 존재하지만, 사용자에게 페이지 번호만 요청받기 위해 인자를 제한했습니다
⬇️ 아래 코드는 pageableDefault를 사용해 페이징 정보를 받는 예제입니다

  • page : 페이지 번호, 기본값 0
  • size : 페이지 크기, 기본값 20
  • sort : 정렬 기준
 @PageableDefault(size = 30, sort = "id", direction = Sort.Direction.ASC) Pageable page

2️⃣ Service

편의를 위해 PortfolioRepository가 아닌 Artist의 PortfolioList를 사용해 조회 기능을 구현했습니다

  • 포트폴리오 숨김 처리를 위해 portfolio Entity의 isblock field가 true면 반환 리스트에서 제거
  • 리스트를 Page<>로 변환
  • Page 형태로 리턴
 // 포트폴리오 전체 조회
    public PortfolioPageDto getPortfolio(Long artistId, int page) {
	    //아티스트 조회
        Artist artist = artistRepository.findById(artistId)
                .orElseThrow(() -> new GlobalException(ErrorStatus.NOT_EXIST_ARTIST));

		//아티스트의 포트폴리오 리스트 가져오기
        List<Portfolio> portfolioList = artist.getPortfolioList();

        //isblock이면 리스트에서 제거
        portfolioList.removeIf(Portfolio::isBlock);

        //list를 page로 변환
        Page<Portfolio> portfolioPage = getPage(page, portfolioList);

		//pagedto로 반환
        return PortfolioPageDto.from(portfolioPage);
    }

‼️ List를 Page로 바꾸기

위의 코드에서 사용한 getPage 함수입니다

  • PageRequest.of 함수를 사용해 Pageable 인스턴스 생성
    한 페이지의 크기를 30으로 설정해, of 메소드에 페이지 번호와 페이지 크기를 전달
  • Page<> 인스턴스 생성
 private Page<Portfolio> getPage(int page, List<Portfolio> list){
 		//Pageable interface
        Pageable pageable = PageRequest.of(page, 30);

        int start = (int) pageable.getOffset();
        int end = Math.min((start + pageable.getPageSize()), list.size());

        //list를 page로 변환
        return new PageImpl<>(list.subList(start, end),
                pageable, list.size());
    }

➕page 바로 사용

현재 사용하는 예제는 repository를 거치지 않고, list를 page 인스턴스로 가공해 반환하고 있습니다! 추가로, repository를 통해 page인스턴스를 바로 가져오는 기능은 ⬇️ 아래 포스팅을 참조해주세요 😁
https://velog.io/@dooo_it_ly/SprintBoot-JPA-검색-기능-Paging-기능-구현하기


3️⃣ PageDTO

내보낼 PageDto입니다
메인데이터, 현재 페이지 번호, 페이지 크기(30으로 지정) 등의 필드를 지정해 dto로 만들어줍니다

Builder를 사용해 반환하였으며, 메인 데이터 리스트도 stream()을 사용해 dto로 변환해서 사용해줍니다

아래 Page 클래스에 내장된 메소드들을 사용했습니다

  • getSize() : 페이지 크기
  • getNumber() : 현재 페이지 번호
  • getNumberOfElements() : 전체 아이템 개수
  • getTotalPages() : 전체 페이지 개수
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class PortfolioPageDto {
    private List<PortfolioDto> content;
    private int currentPage; //현재 페이지 번호
    private int pageSize; //페이지 크기
    private int totalNumber; //전체 메이크업 개수
    private int totalPage; //전체 페이지 개수

    public static PortfolioPageDto from(Page<Portfolio> page){
        //검색 결과가 없을 시
        if(page.getContent().isEmpty())
            throw new GlobalException(ErrorStatus.SEARCH_NOT_FOUNT);

        List<PortfolioDto> content = page.stream()
                .map(PortfolioDto::from)
                .toList();

        return PortfolioPageDto.builder()
                .content(content)
                .pageSize(page.getSize())
                .currentPage(page.getNumber())
                .totalNumber(page.getNumberOfElements())
                .totalPage(page.getTotalPages())
                .build();
    }
}

4️⃣ Test

  • page = 0
    p1부터 p30까지 30개의 데이터가 dto에 감싸져 리스트 형태로 반환되는 걸 볼 수 있습니다
    35개의 데이터를 가지고 테스트하기 때문에 전체 페이지 개수는 2개입니다


  • page = 1
    p31부터 p35까지 5개의 데이터가 반환됩니다

1개의 댓글

comment-user-thumbnail
2024년 4월 1일

고수신데용

답글 달기