페이징 공부한 내용을 정리해 본다.
출처 : https://freehoon.tistory.com/112
페이징은 여러 곳에서 쓰이므로 재사용성을 위해 객체로 만든다.
Getter/Setter는 생략한다.
public class Pagination {
private int currPageNo;/// 현재 페이지 번호
private int sizePerPage = 10; // 한 페이지당 보여질 리스트 개수
private int totalCnt; // 전체 목록 개수
private int pageCnt; // 전체 페이지 개수
private int startList; // 게시판 시작 번호
private int range = 1; // 페이지 범위. 1 : 1~5 2: 6~10 ...
private int pageSize = 5; // 한 페이지 범위에 보여질 페이지 개수
private int startPage; // 각 페이지 범위의 시작 번호
// 검색
private String searchType; // 검색타입 (글제목, 글쓴이 등등)
private String keyword; // 키워드
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
private String fromDate; // 날짜 기한
private String toDate; // 날짜 기한
private int endPage; // 각 페이지 범위 끝 번호
private boolean prev; // 이전 페이지 여부
private boolean next; // 다음 페이지 여부
// 핵심
public void pageInfo(int currPageNo, int range, int totalCnt) {
this.currPageNo = currPageNo;
this.range = range;
this.totalCnt = totalCnt;
// 전체 페이지 수
this.pageCnt = (int) Math.ceil((double)totalCnt/sizePerPage);
// 시작 페이지
this.startPage = (range - 1) * pageSize + 1;
// 끝 페이지
this.endPage = range * pageSize;
// 게시판 시작 번호
this.startList = (currPageNo - 1) * sizePerPage + 1;
// 이전 버튼 상태
this.prev = range == 1 ? false : true;
// 다음 버튼 상태
this.next = endPage >= pageCnt ? false : true;
if(this.endPage > this.pageCnt) {
this.endPage = this.pageCnt;
this.next = false;
}
}
}
다음 버튼 활성화를 정리하자면, endPage는 range(페이지 범위)를 기준으로 한 마지막 페이지다
pageCnt(전체 페이지)가 9개면 range가 2이므로 endpage는 10이다. pageCnt가 endPage보다 작으니 다음버튼은 비활성화 되어야 한다. endPage를 pageCnt로 바꿔준다.
@RequestMapping(value="/list.action", method= RequestMethod.GET)
public ModelAndView boardList(HttpServletRequest request, ModelAndView mav,
@RequestParam(value = "currPageNo", required = false, defaultValue = "1") String tmpcurrPageNo,
@RequestParam(value = "range", required = false, defaultValue = "1") String tmprange) {
int currPageNo = 0;
int range = 0;
try {
currPageNo = Integer.parseInt(tmpcurrPageNo);
range = Integer.parseInt(tmprange);
} catch(NumberFormatException e) {
currPageNo = 1;
range = 1;
}
List<BoardVO> boardList = null;
String searchType = request.getParameter("searchType");
String keyword = request.getParameter("keyword");
if(searchType == null || (!"subject".equals(searchType) && !"name".equals(searchType)) ) {
searchType = "";
}
if(keyword == null || "".equals(keyword) || keyword.trim().isEmpty() ) {
keyword = "";
}
Map<String, String> paraMap = new HashMap<>();
paraMap.put("searchType", searchType);
paraMap.put("keyword", keyword);
Pagination pg = new Pagination();
pg.setSearchType(searchType);
pg.setKeyword(keyword);
int totalCnt = boardService.boardTotalCnt(pg);
pg.pageInfo(currPageNo, range, totalCnt);
boardList = boardService.boardListPaging(pg);
HttpSession session = request.getSession();
if(!"".equals(searchType) && !"".equals(keyword)) {
mav.addObject("paraMap", paraMap);
}
mav.addObject("pagination", pg);
mav.addObject("boardList", boardList);
mav.setViewName("board/boardList.tiles1");
return mav;
}
RequestParam으로 currPageNo와 range를 받아온다. GET방식의 호출이므로 사용자가 URI에 문자열을 입력하는 장난을 방지하기 위해 try-catch문으로 예외처리를 해준다.
그 밑에 if문으로 URI에 serachType의 장난방지와 keyword의 공백처리를 해준다. serachType과 keyword를 Map에 담아 페이지로 전달해주는 이유는 검색내용을 페이지에 유지하기 위해서이다.
<!-- 페이징 검색 게시글 수 -->
<select id="boardTotalCnt" parameterType="pagination" resultType="int">
select count(*)
from tbl_board
where 1=1
<choose>
<when test="searchType neq ''">
and lower( ${searchType} ) like '%'|| lower(#{keyword}) ||'%'
</when>
</choose>
</select>
<!-- 페이징 리스트 -->
<select id="boardListPaging" parameterType="pagination" resultType="boardVO">
select *
from(
select rownum as rnum, t.*
from
(
select *
from tbl_board
where 1=1
<choose>
<when test="searchType neq ''">
and lower( ${searchType} ) like '%'|| lower(#{keyword}) ||'%'
order by seq desc
</when>
</choose>
) t
where rownum <![CDATA[<=]]> #{startList}+#{sizePerPage}
)
where rnum <![CDATA[>=]]> #{startList}
</select>
where절에 lower( ${searchType} )를 하는 이유는 대소문자 구분 없이 모두 검색해주기 위해 해당 칼럼을 모두 소문자로 바꿔주는 것이다.
<div>
<nav aria-label="Page navigation example" style="margin: auto;">
<ul class="pagination justify-content-center">
<c:if test="${pagination.prev}">
<li class="page-item"><a class="page-link" href="#" onClick="fn_prev('${pagination.currPageNo}', '${pagination.range}', '${pagination.pageSize}')">이전</a></li>
</c:if>
<c:forEach begin="${pagination.startPage}" end="${pagination.endPage}" var="idx">
<li class="page-item <c:out value="${pagination.currPageNo == idx ? 'active' : ''}"/> "><a class="page-link" href="#" onClick="fn_pagination('${idx}', '${pagination.range}')"> ${idx} </a></li>
</c:forEach>
<c:if test="${pagination.next}">
<li class="page-item"><a class="page-link" href="#" onClick="fn_next('${pagination.currPageNo}', '${pagination.range}', '${pagination.pageSize}')" >다음</a></li>
</c:if>
</ul>
</nav>
</div>
<script>
//이전 버튼
function fn_prev(currPageNo, range, pageSize) {
var currPageNo = (range - 1) * pageSize;
var range = range - 1;
var url = "/list.action";
url = url + "?currPageNo=" + currPageNo;
url = url + "&range=" + range;
url = url + "&searchType=" + $("#searchType").val();
url = url + "&keyword=" + $("#keyword").val();
location.href = url;
}
//페이지 번호 클릭
function fn_pagination(currPageNo, range) {
var url = "/list.action";
url = url + "?currPageNo=" + currPageNo;
url = url + "&range=" + range;
url = url + "&searchType=" + $("#searchType").val();
url = url + "&keyword=" + $("#keyword").val();
location.href = url;
}
//다음 버튼 이벤트
function fn_next(currPageNo, range, pageSize) {
var currPageNo = (range * pageSize) + 1;
var range = parseInt(range) + 1;
var url = "/list.action";
url = url + "?currPageNo=" + currPageNo;
url = url + "&range=" + range;
url = url + "&searchType=" + $("#searchType").val();
url = url + "&keyword=" + $("#keyword").val();
location.href = url;
}
</script>
검색 조건이 추가됨에 따라 Pagination 클래스에 변수를 추가하고 url = url + OO을 추가해주는 식으로 확장할 수 있다.