Pagination with JPA

게시글 페이징 처리
SHIN's avatar
Sep 12, 2024
Pagination with JPA
Contents
실습요약
 

1. Spring Data JPA의 PagingAndSortingRepository

💡
  • 기본적으로 페이징과 정렬 기능을 지원하는 인터페이스
  • Pageable 객체를 사용해 간단하게 페이징을 처리 가능
 
Ex 코드
// 1. public interface UserRepository extends PagingAndSortingRepository<User, Long> { Page<User> findAll(Pageable pageable); } Pageable pageable = PageRequest.of(0, 10); // 페이지 번호 0, 페이지 당 10개 데이터 Page<User> page = userRepository.findAll(pageable); List<User> users = page.getContent(); // 페이징된 데이터

2. JpaRepository

💡
  • 더 많은 기능 제공
  • 페이징을 포함, 여러 간편 작업 가능
  • Page나 Slice를 반환하여 페이징 된 데이터 로드 가능
 
Ex 코드
public interface UseRepository extends JpaRepository<User, Long> { Page<User> findByName(String name, Pageable pageable); } Pageable pageable = PageRequest.of(0, 10); Page<User> page = userRepository.findByName("John", pageable); List<User> users = page.getContent(); // Page : 전체 페이지 수, 현재 페이지 번호, 총 데이터 수 등을 제공 // Slice : 전체 페이지 수나 총 데이터 수 없이 다음 페이지가 있는지 여부만 제공
 

3. JPQL과 EntityManager를 사용한 페이징 처리

💡
  • 직접 JPQL 쿼리를 사용해 페이징 처리 가능
  • EntityManagercreateManagercreateQuery()메소드를 사용하여 페이징 쿼리 실행
 
Ex 코드
String jpql = "SELECT u FROM User u WHERE [u.name](http://u.name/) = :name"; TypedQuery<User> query = entityManager.createQuery(jpql, User.class); query.setParameter("name", "John"); query.setFirstResult(0); // 시작 인덱스 (0부터 시작) query.setMaxResults(10); // 한 번에 가져올 데이터 수 List<User> users = query.getResultList();
 

4. Native Query를 사용한 페이징 처리

💡
  • Native SQL Query를 통해서도 페이징 구현 가능
  • 이 경우 SQL 문법을 그대로 사용 가능
 
Ex 코드
@Query(value = "SELECT * FROM users WHERE name = :name LIMIT :limit OFFSET :offset", nativeQuery = true) List<User> findUsersByNameWithPaging(@Param("name") String name, @Param("limit") int limit, @Param("offset") int offset); List<User> users = userRepository.findUsersByNameWithPaging("John", 10, 0);
 
 

5.Json Viewer(https://jsonviewer.stack.hu)를 통한 데이터 시각화

 
notion image
 
notion image
 

 

실습

 

1. Controller

@GetMapping("/") public String list( @RequestParam(name = "title", required = false) String title, @RequestParam(name = "page", required = false, defaultValue = "0") Integer page, HttpServletRequest request) { Page<Board> boardP6 = boardService.게시글목록보기(title, page); // jpa의 Page 객체로 반환 // Request에 Model 추가 request.setAttribute("model", boardP6); request.setAttribute("prev", boardP6.getNumber()-1); request.setAttribute("next", boardP6.getNumber()+1); return "board/list"; }
 
 

2. Service

public Page<Board> 게시글목록보기(String title, int page) { // 한 페이지당 3개 게시글 보여줌, ID 기준 내림차순, PageRequest.of를 통해 페이징 처리를 위한 Pageable 객체 생성 Pageable pageable = PageRequest.of(page, 3, Sort.Direction.DESC, "id"); if (title == null) { // 검색어 없으면 findAll(pageable) 메소드를 사용해 모든 게시글 가져옴 return boardRepository.findAll(pageable); } else { // 검색어 있으면 mFindAll(title, pageable)로 제목에 해당 검색어 포함 게시글 목록 가져옴 return boardRepository.mFindAll(title, pageable); } }
 
 

3. Repository

public interface BoardRepository extends JpaRepository<Board, Integer> { @Query("select b from Board b where b.title like %:title% order by b.id desc") Page<Board> mFindAll(@Param("title") String title, Pageable pageable); @Query("select b from Board b join fetch b.user left join fetch b.replies r left join fetch r.user where b.id=:id") Optional<Board> mFindByIdWithReply(@Param("id") int id); @Query("select b from Board b join fetch b.user u where b.id=:id") Optional<Board> mFindById(@Param("id") int id); }
💡
  • mFindAll : 제목에 검색어가 포함된 게시글을 ID 기준으로 내림차순 정렬하여 페이징 처리된 결과를 반환하는 커스텀 쿼리. @Query 어노테이션으로 JPQL로 쿼리 작성. %:title%은 제목에 해당 검색어가 포함된 항목을 검색
  • mFindByIdWithReply: 게시글을 조회하면서 해당 게시글의 유저 정보 및 댓글과 관련된 유저 정보까지 fetch join으로 가져옴
  • mFindById: 특정 ID에 해당하는 게시글과 그 유저 정보를 가져옴
 
 

4. mustache

<div class="container p-5"> <div class="d-flex justify-content-end mb-2"> <form action="/" method="get" class="d-flex col-md-3"> <input class="form-control me-2" type="text" placeholder="Search" name="title"> <button class="btn btn-primary">Search</button> </form> </div> {{#model.content}} <div class="card mb-3"> <div class="card-body"> <h4 class="card-title mb-3">{{title}}</h4> <a href="/board/{{id}}" class="btn btn-primary">상세보기</a> </div> </div> {{/model.content}} <ul class="pagination d-flex justify-content-center"> <li class="page-item"><a class="page-link" href="?page={{prev}}">Previous</a></li> <li class="page-item"><a class="page-link" href="?page={{next}}">Next</a></li> </ul> </div>
💡
  • {{#model.content}}: modelcontent 리스트를 순회하며 게시글 출력 model.contentPage 객체의 게시글 목록.
  • {{prev}}, {{next}}: 이전 페이지와 다음 페이지 링크 제공. Controller에서 설정한 prevnext를 사용
 
 
 

5. Response DTO

@Data public static class PageDTO { private Integer number; // 현재 페이지 private Integer totalPage; // 전체 페이지 개수 private Integer size; // 한 페이지에 보여줄 아이템 개수 private Boolean first; // 첫 번째 페이지 여부 private Boolean last; // 마지막 페이지 여부 private Integer prev; // 이전 페이지 private Integer next; // 다음 페이지 private List<Content> contents; // 페이지에 포함된 게시글 리스트 @Data class Content { private Integer id; // 게시글 ID private String title; // 게시글 제목 } }
💡
  • Page 객체에서 필요한 정보를 추출하여 클라이언트로 전달할 때 사용
 
 
 

 

요약

💡
  1. 클라이언트가 검색어와 페이지 번호를 전송하면 Controller에서 받아 Service에 전달
  1. Service는 검색어 유무에 따라 적절한 Repository 메소드 호출 후 데이터 로드
  1. Repository는 JPA를 통해 DB에서 데이터 조회, 결과를 Page 객체 반환
  1. Controller는 이 데이터를 mustache에 전달해 페이징된 게시글 목록을 화면에 렌더링
 
Share article

SHIN