[Spring] 게시판 페이징(Paging) 처리
테이블에서 데이터 List를 가져오는 쿼리문을 페이징 처리가 가능하도록 작성
테이블 구조(Oracle)
CREATE SEQUENCE board_num;
CREATE TABLE board_tbl(
bno NUMBER(10, 0),
title VARCHAR2(200) NOT NULL,
content VARCHAR2(2000) NOT NULL,
writer VARCHAR2(50) NOT NULL,
regdate DATE DEFAULT SYSDATE,
updatedate DATE DEFAULT SYSDATE
);
ALTER TABLE board_tbl ADD CONSTRAINT pk_board PRIMARY KEY(bno);
Mapper.xml과 매핑할 Mapper 인터페이스 작성
public interface Mapper{
public List<BoardVO> getListPaging(Criteria cri);
}
Hint(Oracle이 데이터를 스캐닝할 때, 작성자가 직접 최적의 실행경로를 지정해주는 것)를 사용해 정렬하고,
페이징 처리된 데이터를 가져올 수 있도록 SQL문 작성(Mapper.xml)
<mapper namespace="mapper인터페이스 경로.Mapper">
<select id="getListPaging" resultType="VO객체 경로.BoardVO">
<![CDATA[
SELECT bno, title, content, writer, regdate, updatedate FROM
(SELECT /*+ INDEX_DESC(board_tbl pk_board) */
rownum rn, board_tbl.* FROM board_tbl
WHERE rownum <= 페이지 번호 * 보여줄 글 개수) tbl
WHERE rn > (페이지 번호 - 1) * 보여줄 글 개수
]]>
</select>
</mapper>
/*+ INDEX_DEESC(테이블명 제약조건명) */
: 지정된 index를 내림차순으로 정렬
(MyBatis에서는 CDATA를 작성해 '<' 등의 기호가 괄호인지 비교연산자인지 알아볼 수 있도록 작성)
Criteria 객체를 생성해 페이지 번호와 한 페이지당 몇 개의 글을 보여줄지 지정
@Getter
@Setter
@ToString
public class Criteria{
private int pageNum; // 페이지 번호
private int amount; // 한 페이지 당 나타낼 글 개수
public Criteria(){ // 생성자 오버로딩 - 들어온 페이지 정보가 없으면
this(1, 10); // 1페이지 10개의 게시물을 기본값으로 지정
}
public Criteria(int pageNum, int amount){
this.pageNum = pageNum; // 들어온 정보를 기반으로 수치 지정
this.amount = amount;
}
}
DTO(Data Transfer Object, 데이터 전달 객체)를 생성해 DB에 있는 정보를 토대로 가공한 데이터를 전달
2021.09.25 - [Spring] - [Spring] 페이징 처리를 위한 공식(시작 페이지와 끝 페이지)
[Spring] 페이징 처리를 위한 공식(시작 페이지와 끝 페이지)
버튼 개수는 1부터 10까지, 10개로 고정 끝 페이지 번호를 구하는 공식 int endPage = (int)(Math.ceil(현재 페이지 / (double)버튼 개수) * 버튼 개수); 현재 보고있는 페이지가 253일 경우, (현재 페이지 / (do..
wheneveryouwantsz.tistory.com
@Getter
@ToString
public class PageDTO{
private int btnNum; // 페이지네이션 버튼 개수(10개)
private int startPage; // 페이지 시작 번호(1~10에서는 1, 11~20에서는 11)
private int endPage; // 페이지 끝 번호(1~10에서는 10, 11~20에서는 20)
private boolean prev, next; // 이전, 이후 버튼 유무(1~10에서는 이전버튼이 없고, 마지막에서는 이후버튼이 없음)
private int total; // 전체 게시글 개수(DB에 저장된 데이터 개수)
private Criteria cri; // 페이지와 페이지당 글 개수
// 위 선언한 변수를 가공하기위한 생성자
public PageDTO(Criteria cri, int total, int btnNum){
this.cri = cri;
this.total = total;
this.btnNum = btnNum;
this.endPage = (int)(Math.ceil(cri.getPageNum() / (double)this.btnNum) * this.btnNum);
this.startPage = this.endPage - this.btnNum + 1;
int realEnd = (int)(Math.ceil((total * 1.0) / cri.getAmount()));
if(realEnd < this.endPage) {
this.endPage = realEnd;
}
// 이전 버튼은 시작 번호가 1이 아닐 경우에만 나타남
this.prev = this.startPage == 1 ? false : true;
// 이후 버튼은 endPage가 realEnd보다 작을 때만 나타남
this.next = this.endPage < realEnd;
}
}
Mapper를 호출할 Service 인터페이스
// service 인터페이스
public interface Service{
public List<BoardVO> getListPaging(Criteria cri);
}
// service 인터페이스 구현
@Service
public ServiceImpl implements Service{
@Autowired
private Mapper mapper;
@Override
public List<BoardVO> getListPaging(Criteria cri){
// cri의 정보인 pageNum, amount를 받아오면 해당 정보를 이용해 Mapper의 getListPaging을 호출
return mapper.getListPaging(cri);
}
}
Service를 호출해 웹브라우저의 요청을 담당하는 Controller
@Controller
public class Controller{
@Autowired
private Service service;
// list라는 주소로 연결해 게시글 목록을 보여줌
@GetMapping("/list")
public void list(Criteria cri, Model model){
// List를 만들어 service 호출
List<BoardVO> board = service.getListPaging(cri);
// 데이터 전달
model.addAttribute("board", board);
// DTO를 생성해 필요한 정보들을 넣어줌
PageDTO dto = new PageDTO(cri, 전체 데이터개수, 페이지당 글 개수);
model.addAttribute("dto", dto);
}
}
Controller 수행 후, 이동하게 될 list.jsp - 버튼은 부트스트랩을 이용
<nav aria-label="...">
<ul class="pagination">
<!-- 이전 버튼 - dto의 prev가 true 일때만 출력 -->
<c:if test="${dto.prev }">
<li class="page-item">
<!-- 이전 버튼을 누르면 현재 보고있는 페이지의 시작페이지보다 하나 더 작게 -->
<a class="page-link" href="/list?pageNum=${dto.startPage - 1 }">Previous</a>
</li>
</c:if>
<!-- 번호 버튼 -->
<c:forEach var="pageNum" begin="${dto.startPage }" end="${dto.endPage }">
<!-- 현재 페이지는 active로 표시 -->
<li class="page-item ${dto.cri.pageNum == pageNum ? 'active' : '' }">
<a class="page-link" href="/list?pageNum=${pageNum }">${pageNum }</a>
</li>
</c:forEach>
<!-- 다음 페이지 버튼 -->
<c:if test="${dto.next }">
<li class="page-item">
<!-- 다음 버튼을 누르면 현재 보고있는 페이지의 끝페이지보다 하나 더 크게 -->
<a class="page-link" href="/list?pageNum=${dto.endPage + 1 }">Next</a>
</li>
</c:if>
</ul>
</nav>