GitHub

https://github.com/Backcoder-June

BackCoder 기록 그리고 숙달

Back to the Spring

JPA Paging 처리 (Page, Pageable)

Backcoder 2023. 1. 18. 20:38

JpaRepository 를 상속해서 사용할 수 있는 메소드 중 

findAll() 메소드는 

Page<T> findAll(Pageable pageable);

위와 같이 Page 처리에 대한 정보를 담고있는 Pageable 객체를 매개변수로 받아서, 

목표로 하는 T 객체를 페이징처리하여 Page 객체타입으로 받아내는 기능을 제공합니다.

 

Pageable

Pageable pageable = PageRequest.of(
        pageRequestDTO.getPage() - 1, pageRequestDTO.getSizePerPage(),
        Sort.Direction.DESC, "createdDate"
);

PageRequest (자식) 객체를 이용해서, 

PageRequest.of( 1.현재페이지, 2.페이지당 데이터개수, 3.정렬 순서, 4."정렬기준필드" ); 

정보를 토대로, 어떻게 페이징 처리를 할것인 지 에 대한 정보를 pageable 객체에 담아둡니다. 

 

final Page<PostEntity> pageData = postRepository.findAll(pageable);
List<PostEntity> allPosts = pageData.getContent();

페이징에 대한 정보를 담고있는 pageable 객체를 이용해서, findAll( ) 메소드를 쓰면 

Page<목표DTO> 객체에 

현재 페이지, 페이지당 데이터개수, 정렬기준으로 정렬된 데이터를 Page 객체에 담아둡니다. 

.getContent() 메소드를 사용해 해당하는 데이터들을 뽑아 쓸 수 있고, 

 

pageData. getTotalElements()  : 총 데이터 개수 

pageData.getTotalPages() : 총 페이지 수 

 

부가적인 페이징에 관한 정보들 또한 사용할 수 있습니다. 

 

 

위에서, Pageable 객체를 만들 때 필요한 페이징에 대한 정보

 

1. 현재 페이지 

2. 페이지당 데이터 개수 

 

위의 정보들이 있어야 Pageable 객체를 만들고, Page 객체를 만들어 사용할 수 있으므로

위의 정보들을 받아올 수 있는 Page정보용 DTO를 만들어 사용합니다.

 

@ToString @Getter @Setter
public class PageRequestDTO {
    private int page;  // 요청한 페이지 번호
    private int sizePerPage;  // 한페이지 당 데이터 개수

    // 기본생성자 => 초기 세팅 => int 기본 값 0 으로 잡혀있으니까 기본 값 1로 설정, sizeperPage 설정
    public PageRequestDTO() {
        this.page = 1;
        this.sizePerPage = 3;
    }

    // Setter 이용 트롤 처리
    public void setPage(int page) {
        // 예외처리 => 공격자들이 페이지 번호를 음수를 줘버리거나, 이상한 값을 줄 경우, page 1 로 설정. 그리고 날려보냄
        if (page < 0 || page > Integer.MAX_VALUE) {
            this.page = 1;
            return;
        }
        this.page = page;   // 기본 세터
    }

    public void setSizePerPage(int sizePerPage) {
        if (sizePerPage < 10 || sizePerPage > 100) {
            this.sizePerPage = 10; // 이거 공격자가 선넘게 이상하게 고쳐두면  10 으로 설정.
            return;
        }
        this.sizePerPage = sizePerPage;
    }
}

 

클라이언트에서 page 에 대한 정보를 이 페이지정보DTO로 받아서

=> Pageable 객체를 생성, 이 객체를 이용해서

=> Page<목표Entity> 객체를 생성

=> .getContent( ) 등 메소드를 이용해 페이지 처리 된 데이터를 사용  

@RequestMapping(value = "", method = RequestMethod.GET)
public ResponseEntity<?> getAllPosts(PageRequestDTO pageRequestDTO) {
	PostListResponseDTO allList = postService.getAllList(pageRequestDTO);

 

- 이렇게 하면, 

총 페이지 수,

총 데이터 수,

현재 페이지,

페이지 당 데이터 수,

해당하는 데이터 

를 가지고 있습니다. 

 

서버단에서는 이 정보들을 이용해

페이징에서 현재 페이지 기준, prev / next 처리를 할 수 있도록  

 

1. 페이징 startPage 

2. 페이징 endPage 

3. 실제 endPage

4. prev 활성화 boolean 

5. next 활성화 boolean 

 

등의 정보를 처리해 클라이언트 단으로 보내줍니다. 

 

@ToString @Setter @Getter
@Builder @NoArgsConstructor @AllArgsConstructor
public class PageResponseDTO<T> {
    // Paging 처리 할 수 있는 정보들 ( startPage, endPage, currentPage, prev, next, totalCount ) 넘겨줘야 함
    // current 페이지 기준으로
    // start end 는 페이징 번호 몇번 부터 몇번까지 보여줄 지
    // prev next 는 이전 다음 활성화

    private int startPage;
    private int endPage;
    private int currentPage;
    private boolean prev;
    private boolean next;
    private int totalCount;

    // prev / next 당 보여 줄 페이지 수
    private static final int PAGE_COUNT = 3;

    // 생성자 설정
    public PageResponseDTO(Page<T> pageData) {
        // index 니까 +1 해줘야 클라에서 보이는 진짜 currentPage 숫자
        this.currentPage = pageData.getPageable().getPageNumber() + 1; //Pageable 쓰면 PageNumber 가져올 수 있음

        // current가 34 번째 페이지 라면, 페이지 개수 10 개로 잡았을 때, 3.4 ceil => 4 => 40 페이지로 endPage를 만들겠다.
        // 나눌 때, int / int 로 나누면 int 로만 나오니까, double 변환 해줘야 3.4 로 나오겠지
        this.endPage = (int)Math.ceil((double)currentPage / PAGE_COUNT) * PAGE_COUNT;
        // 근데 이거 찐 마지막 페이징에서는 적용 안됨 => 보정설정

        this.startPage = endPage - PAGE_COUNT + 1;
        this.totalCount = (int)pageData.getTotalElements(); //long 타입이라 int 로 다운그레이드

//        int realEnd = (int) Math.ceil((double) totalCount / pageData.getSize());
        // JPA 로는
        int realEnd = pageData.getTotalPages(); // 실제 end 페이지 구해줌
        
        this.prev = startPage > 1;  // startPage 가 1 보다 크면 true 다. 1이면 false 처리

        if (realEnd < endPage) {
            this.endPage = realEnd;
        }
        // 마지막 구간에서만 보정해주는 설정 - endPage 를 realEnd 페이지로 설정
        this.next = endPage < realEnd;
    }
}

 

클라이언트 단으로 보낼 때는 

 

1. 페이징 처리 된 DTO 데이터 자료 (List) 

2. start,endPage 등 정보를 보낼 pageResponseDTO 

 

등을 같이 보내야 하므로, 

이들을 포장해서 보내줄 큰 DTO 객체를 만들어, 여기에 담아서 클라이언트 단에 보내줍니다. 

public class PostListResponseDTO {

    private int count;
    // 페이지정보 객체
    private PageResponseDTO pageInfo;
    // 데이터 List
    private List<PostResponseDTO> posts;
}
PostListResponseDTO listResponseDTO = PostListResponseDTO.builder()
        .count(responseDTOList.size())
        .pageInfo(new PageResponseDTO<PostEntity>(pageData))
        .posts(responseDTOList)
        .build();

return listResponseDTO;

 

클라이언트단에서는 start, endPage, realEndPage, 페이징 된 dtoList 데이터 등을 받아 

페이징 처리를 해주게 됩니다. 

 

 

JpaRepository를 상속했을 때 사용할 수 있는

Page<T>  findAll(Pageable pageable)

메소드를 이용해 페이징 처리에 도움을 받을 수 있지만, 그래도 페이징은 역시나 복잡한 영역인 듯 합니다.

 

eGov 프레임워크에서도 Paging 처리를 위해 제공되는 기능들이 있어서 활용할 수 있는데 

다음에는 eGov 환경에서 페이징처리 구현을 시도해 봐야겠습니다. 

https://alisyabob.tistory.com/243

 

전자정부프레임워크 Pagination 페이징처리

전자정부프레임워크 Pagination 페이징처리 기본적으로 전자정부 프레임워크에서는 페이징 처리를 편하게 하기 위해 태그를 제공한다. PaginationInfo는 렌더링에 필요한 데이터가 담겨져있는 빈 클

alisyabob.tistory.com