게시판에 100개의 글이 있는 상황에서는 페이징 처리를 하지 않으면 그냥 보여주면 너무 많다. 이런 상황에서는 페이징 처리
가 필요하다. 그러면 한 페이지에 글을 10개씩만 보여줄 수 있다. 예를 들어 1번페이지 1~10개 2번 페이지에는 11~20개 이렇게 보여주는 것이다.
JPA에는 이 페이징 기능을 지원한다. 2가지 방법을 지원한다.
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);
}
컨트롤러에서는 두 가지로 처리할 수있다.
@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
을 사용할거면 다음과 같이 하면 됩니다.
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);
}