페이징 처리
JSP 와 같은 SSR 에서의 페이징처리는 조금 까다로운데요.
React 와 같은 CSR 에서의 페이징처리는 라이브러리를 사용하면 됩니다만.. JSP에선 그럴 수 없죠..
결과화면은 밑과 같습니다.
PageReq
@Getter
@Setter
@ToString
@NoArgsConstructor
//@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class PageReq {
/** 현재 페이지 번호 : page == number */
private int page;
/** 페이지당 출력할 데이터 개수 */
private int size;
// offset 개수 : 1st 데이터로 부터 얼마나 떨어져 있는지 개수
// private Integer offset = page * size;
public PageReq(int page, int size) {
this.page = page;
this.size = size;
}
}
PageRes
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class PageRes<Object> {
// 쿼리 결과를 저장할 배열
private List<Object> content;
// 현재 페이지 번호 : 0부터 시작
private int number;
// 테이블 총 건수
private long totalElements;
// 테이블 총 건수 / 페이지당 출력할 데이터 개수(size)
// 총페이지수
private int totalPages;
// 1페이지당 개수
private int size;
// 시작 블럭 페이지 번호
private int startPage;
// 끝 블럭 페이지 번호
private int endPage;
public PageRes(List<Object> content, int number, long totalElements, int size) {
this.content = content;
this.number = number; // 현재 페이지 번호
this.totalElements = totalElements; // 총 데이터 개수
this.size = size; // 1페이지 당 개수
this.totalPages = (int) Math.ceil((double) totalElements / size); // 총페이지 수 (소수점이 나오면 +1 페이지 더함)
this.startPage = ((int) Math.floor((double)(number) / size) * size) + 1; // 현재 페이지 블럭의 시작 페이지 번호
this.startPage = (this.startPage < 1)? 1 : this.startPage; // 시작페이지 번호 보정(1보다 작을 수 없음)
this.endPage = this.startPage + (size - 1); // 현재 페이지 블럭의 끝 페이지 번호
this.endPage = (this.endPage > this.totalPages ) ? this.totalPages : this.endPage; // 끝페이지 보정(총페이지수보다 클 수 없음)
}
// 데이터가 없으면 false, 있으면 true
public boolean isEmpty() {
if (totalElements > 0) {
return false;
} else {
return true;
}
}
}
사용해 보기
저와 같이 JPA를 사용하는 분은 Page와 Pageable 객체가 따로 존재하기 때문에 PageRes 만 있으면 사용이 가능합니다.
이야기에 앞서 Board 라는 객체가 있다고 가정합니다.
1. JPA
Repository
Mysql 과 Oracle DB 와 같은 데이터베이스들은 nativeQuery 가 가능합니다.
Page 와 Pageable 객체가 따로 존재하니 사용해 봅시다
다만, Pageable 객체의 import 에 주의하여 사용합시다.
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@Query(value = "select * from board", countQuery = "SELECT COUNT(*) from board", nativeQuery = true)
public Page<Board> findAllPage(Pageable page);
Service
서비스 단에서는 PageRes 에 넣어 보내보죠 따로 중요한 것은 없습니다.
메서드
Pageable (Spring Data Core 3.2.2 API)
isUnpaged default boolean isUnpaged() Returns whether the current Pageable does not contain pagination information. Returns:
docs.spring.io
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
/**
* 페이징 처리가 된 모든 게시물 보기
* @param pageable
* @return PageRes<Board>
*/
public PageRes<Board> findAll(Pageable pageable) {
// 1. DB 조회
Page<Board> page = boardRepository.findAllPage(pageable);
// VIEW(뷰) 에서 페이징 처리를 할 PageRes 객체 생성
PageRes<Board> pageRes = new PageRes<>
(page.getContent(), page.getNumber(), page.getTotalElements(),page.getSize());
return pageRes;
}
Controller
마지막 컨트롤러 단입니다.
PageRequest , Pageable , Model 의 import 에 유의 합니다.
따로 중요 사항은 없습니다.
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.ui.Model;
...
@Autowired
BoardService boardService;
/**
* 모든 게시물 찾아보기
*
* @param model
* @param page
* @param size
*/
@GetMapping("/")
public String index(Model model, @RequestParam(value = "page", defaultValue = "0") int page,
@RequestParam(value = "size", defaultValue = "3") int size) {
// 1. 페이징 처리가 될 Pageable 객체 생성
Pageable pageable = PageRequest.of(page, size);
// 2. 서비스 호출
PageRes<Board> pageRes = boardService.findAll(pageable);
model.addAttribute("컨텐츠가 들어가는 부분", pageRes.getContent());
model.addAttribute("currentPage", pageRes.getNumber()); // 현재페이지 번호
model.addAttribute("totalItems", pageRes.getTotalElements()); // 전체테이블 건수
model.addAttribute("totalPages", pageRes.getTotalPages()); // 전체 페이지개수
model.addAttribute("startPage", pageRes.getStartPage()); // 현재 시작 페이지 번호
model.addAttribute("endPage", pageRes.getEndPage()); // 현재 끝 페이지번호
return "index";
}
...
2. Mybatis
Mybatis 는 offset 쿼리가 중요합니다. 이 예제에서는 PageRes , PageReq 둘 다 사용하게 됩니다.
Repository (DAO)
DAO 에서는 두개의 셀렉트 문을 날릴 준비를 합니다.
@Mapper
public interface Dao {
/**
* 전체 조회 (Like 조회)
* @param ename
* @param pageReq
* @return
*/
public List<Board> findAll(PageReq pageReq);
/**
* @param ename
* @return
*/
long count();
}
mapper/..xml
xml 파일의 정의입니다 아시다시피 여기에서 select 문을 날려줍니다.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mybatisexam.dao.Dao">
<select id="findAll" parameterType="PageReq" resultType="Board">
SELECT * FROM Board
OFFSET #{pageReq.page} * #{pageReq.size} ROWS FETCH FIRST #{pageReq.size} ROWS ONLY
</select>
<select id="count" resultType="long">
SELECT COUNT(*) FROM Board
</select>
</mapper>
Service
서비스 단은 JPA 와 비슷합니다 이제부터 크게 차이가 없습니다.
@Service
public class Service {
@Autowired
Dao dao;
public PageRes<Board> findAll(PageReq pageReq) {
List<Board> list = dao.findAll(ename, pageReq);
long totalCount = dao.count(ename);
PageRes<Emp> pageRes = new PageRes<>(list, pageReq.getPage() , totalCount , pageReq.getSize());
return pageRes;
}
}
Controller
위와 동일합니다.
import org.springframework.ui.Model;
...
@Autowired
Service Service;
@GetMapping("/")
public String findEmpByEname(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "3") int size,
Model model
) {
PageReq pageReq = new PageReq(page,size);
PageRes<Emp> pageRes = service.findAll(pageReq);
model.addAttribute("컨텐츠",pageRes.getContent()); // 컨텐츠 배열
model.addAttribute("currentPage",pageRes.getNumber()); // 현재페이지 번호
model.addAttribute("totalItems",pageRes.getTotalElements()); // 전체테이블 건수
model.addAttribute("totalPages",pageRes.getTotalPages()); // 전체 페이지개수
model.addAttribute("startPage",pageRes.getStartPage()); // 현재 시작 페이지 번호
model.addAttribute("endPage",pageRes.getEndPage()); // 현재 끝 페이지번호
return "/";
}
...
종료!!
이것으로 종료!!!
좀 해맸던 부분이였던것 같습니다. 제가 예전 문서를 이상하게 써놯서 다시 확인용도로 써봤습니다.
이 전꺼는 다 지워버려야지 ㅋㅋㅋㅋㅋ
'SpringBoot > code' 카테고리의 다른 글
Springboot - OAuth2.0 Google API , kakao API , Naver API 로그인 (0) | 2023.12.14 |
---|---|
SpringBoot - MyBatis - 다이나믹 SQL 작성 (0) | 2023.10.16 |
SpringBoot - model / common package (0) | 2023.10.12 |
SpringBoot - Optional (클래스) 예제 (1) | 2023.10.11 |
SpringBoot - RedirectView (클래스) (0) | 2023.10.10 |