페이징

유요한·2023년 9월 28일
1

JPA

목록 보기
7/10
post-thumbnail

페이징이란?

게시판에 100개의 글이 있는 상황에서는 페이징 처리를 하지 않으면 그냥 보여주면 너무 많다. 이런 상황에서는 페이징 처리가 필요하다. 그러면 한 페이지에 글을 10개씩만 보여줄 수 있다. 예를 들어 1번페이지 1~10개 2번 페이지에는 11~20개 이렇게 보여주는 것이다.

  • 페이징 기능에는 정렬 기능이 포함되어 있다.
    글의 id순, 최신순, 이름순 등으로 정렬하여 출력할 수 있다.

방법

JPA에는 이 페이징 기능을 지원한다. 2가지 방법을 지원한다.

  • Pageable 사용
  • PageRequest 사용
    PageRequest는 Pageable의 구현체

기능

Page에는 다음과 같은 정보가 있다.

List<User> users = result.getContent();     // User들에 대한 정보
int nowPageNumber = result.getNumber();     // 현재 페이지 번호
int totalPages = result.getTotalPages();    // 전체 페이지 수
int pageSize = result.getSize();        // 한 페이지에 출력되는 데이터 개수
boolean hasNextPage = result.hasNext();     // 다음 페이지 존재 여부
boolean hasPreviousPage = result.hasPrevious(); // 이전 페이지 존재 여부
boolean isFirstPage = result.isFirst();     // 첫번째 페이지 인지
boolean isLastPage = result.isLast();       // 마지막 페이지 인지

예제

@Repository
public interface ItemRepository extends JpaRepository<ItemEntity, Long> {
    ItemEntity deleteByItemId(Long itemId);

    // 페이징 처리를 위해서
    Page<ItemEntity> findAll(Pageable pageable);
}

서비스

   // 전체 상품 보여주기
    public Page<ItemDTO> getItems(Pageable pageable) {
        Page<ItemEntity> all = itemRepository.findAll(pageable);
        // 각 아이템 엔티티를 ItemDTO로 변환합니다.
        // 이 변환은 ItemDTO::toItemDTO 메서드를 사용하여 수행됩니다.
        return all.map(ItemDTO::toItemDTO);
    }

컨트롤러에서는 두 가지로 처리할 수있다.

  1. 상세한 자료를 처리
   @GetMapping("/")
    public ResponseEntity<?> getItems(
            // SecuritConfig에 Page 설정을 한 페이지에 10개 보여주도록
            // 설정을 해서 여기서는 할 필요가 없다.
            @PageableDefault(sort = "itemId", direction = Sort.Direction.DESC)
            Pageable pageable) {
        Page<ItemDTO> items = itemService.getItems(pageable);

        Map<String, Object> response = new HashMap<>();

        // 현재 페이지의 아이템 목록
        response.put("items", items.getContent());
        // 현재 페이지 번호
        response.put("nowPageNumber", items.getNumber());
        // 전체 페이지 수
        response.put("totalPage", items.getTotalPages());
        // 한 페이지에 출력되는 데이터 개수
        response.put("pageSize", items.getSize());
        // 다음 페이지 존재 여부
        response.put("hasNextPage", items.hasNext());
        // 이전 페이지 존재 여부
        response.put("hasPreviousPage", items.hasPrevious());
        // 첫 번째 페이지 여부
        response.put("isFirstPage", items.isFirst());
        // 마지막 페이지 여부
        response.put("isLastPage", items.isLast());

        return ResponseEntity.ok().body(response);
    }

프론트엔드에서 자세한 페이지 정보를 처리해야 할 때 유용하며, 각 페이지 정보를 명시적으로 표현하기 위해 사용됩니다. 또한 클라이언트 측에서 특정 데이터를 더 쉽게 조작하고 사용할 수 있습니다.

프론트는 다음과 같이 받을 수 있다.

// JSON 응답 예시
{
 "items": [/* 아이템 목록 */],
 "nowPageNumber": 1,
 "totalPages": 5,
 "pageSize": 10,
 "hasNextPage": true,
 "hasPreviousPage": false,
 "isFirstPage": true,
 "isLastPage": false
}

// 프론트엔드에서 사용 예시
const response = { /* 위의 JSON 응답 예시 */ };
const items = response.items; // 아이템 목록
const nowPageNumber = response.nowPageNumber; // 현재 페이지 번호
const totalPages = response.totalPages; // 전체 페이지 수
const pageSize = response.pageSize; // 한 페이지에 출력되는 데이터 개수
const hasNextPage = response.hasNextPage; // 다음 페이지 존재 여부
const hasPreviousPage = response.hasPreviousPage; // 이전 페이지 존재 여부
const isFirstPage = response.isFirstPage; // 첫 번째 페이지 여부
const isLastPage = response.isLastPage; // 마지막 페이지 여부

프론트엔드에서는 JSON 응답 객체의 각 키에 접근하여 필요한 정보를 추출하고 활용할 수 있습니다. 각 키는 의미 있는 정보를 가지고 있으므로, 프론트엔드 개발자는 이러한 키를 통해 데이터를 이해하고 활용할 수 있을 것입니다.

    // 전체 상품 보여주기
    @GetMapping("/")
    public ResponseEntity<?> getItems(
            @PageableDefault(size = 10, sort = "itemId", direction = Sort.Direction.DESC)
            Pageable pageable) {
        Page<ItemDTO> items = itemService.getItems(pageable);
        return ResponseEntity.ok().body(items);
    }

간결하고 페이지 정보를 직접 처리하지 않아도 되기 때문에 프론트엔드 코드가 더 간단해질 수 있습니다. 페이지 정보를 클라이언트에게 노출하지 않고 간단한 데이터만 반환하고자 할 때 사용될 수 있습니다.

@PageableDefault
@PageableDefault로 한 화면에 몇개 보여줄지 그리고 뭐를 기준으로 할지 오름차순, 내림차순을 정할 수 있다.

  • size
    한 화면에 몇개를 보여줄지 나타낸다.

  • sort
    뭐를 기준으로 할지 나타내는데 itemId를 하면 id를 기준으로 정하는거다. 여기서는 테이블 기준이 아니라 객체 기준이다.

여기서 size = 10를 생략하려면 SecurityConfig에 다음과 같이 설정하면 된다.

 @Bean
    public PageableHandlerMethodArgumentResolverCustomizer customize() {
        return p -> {
            p.setOneIndexedParameters(true);    // 1 페이지 부터 시작
            p.setMaxPageSize(10);       // 한 페이지에 10개씩 출력
        };
    }

JPQL Fetch Join과 페이징 처리

여기서 JPQL을 사용할거면 다음과 같이 하면 됩니다.

package com.example.shopping.repository.board;

import com.example.shopping.entity.board.BoardEntity;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.util.Optional;
/*
 *   writer : 유요한
 *   work :
 *          게시글 레포지토리
 *          Spring Data JPA 방식을 사용하였고 fetch Join을 위하여
 *          JPQL을 사용했습니다.
 *   date : 2024/01/09
 * */
@Repository
public interface BoardRepository extends JpaRepository<BoardEntity, Long> {
    @Query("select b from board b" +
            " join fetch b.member " +
            " join fetch b.item " +
            " where b.boardId = :boardId")
    Optional<BoardEntity> findByBoardId(@Param("boardId") Long boardId);

    void deleteByBoardId(Long boardId);
    void deleteAllByMemberMemberId(Long memberId);

    @Query(value = "select b from board b" +
            " join fetch  b.member " +
            " join fetch b.item " +
            " where (:searchKeyword is null or b.title like %:searchKeyword%)" +
            " order by b.boardId DESC ",
            countQuery = "select count(b) from board b where (:searchKeyword is null or b.title like %:searchKeyword%)")
    Page<BoardEntity> findByTitleContaining(Pageable pageable,
                                            @Param("searchKeyword") String searchKeyword);

    @Query(value = "select b from board b" +
            " join fetch b.member " +
            " join fetch b.item " +
            " order by b.boardId DESC ",
            countQuery = "select count(b) from board b")
    Page<BoardEntity> findAll(Pageable pageable);


    @Query(value = "select  b from board  b " +
            " join fetch b.member " +
            " join fetch b.item " +
            " where b.member.email = :email and (:searchKeyword is null or b.title like %:searchKeyword%)" +
            " order by b.boardId DESC ",
            countQuery = "select count(b) from board b " +
                    "where b.member.email = :email and (:searchKeyword is null or b.title like %:searchKeyword%)")
    Page<BoardEntity> findByMemberEmailAndTitleContaining(@Param("email") String email,
                                                          Pageable pageable,
                                                          @Param("searchKeyword") String searchKeyword);


    @Query(value = "select b from board b " +
            " join fetch b.member " +
            " join fetch b.item " +
            "where b.member.nickName = :nickName and (:searchKeyword is null or b.title like %:searchKeyword%)" +
            " order by b.boardId desc ",
            countQuery = "select count(b) from board b " +
                    "where b.member.nickName = :nickName and (:searchKeyword is null or b.title like %:searchKeyword%)")
    Page<BoardEntity> findByMemberNickNameAndTitleContaining(@Param("nickName") String nickName,
                                                             Pageable pageable,
                                                             @Param("searchKeyword") String searchKeyword);

    @Query(value = "select b from board b " +
            " join fetch b.member " +
            " join fetch b.item " +
            "where b.item.itemId = :itemId" +
            " order by b.boardId desc ",
            countQuery = "select count(b) from board b where b.item.itemId = :itemId")
    Page<BoardEntity> findAllByItemItemId(@Param("itemId") Long itemId, Pageable pageable);

}
profile
발전하기 위한 공부

0개의 댓글