Map<String, Object> map = new HashMap<String, Object>();
map.put("searchType", searchType);
map.put("searchTxt", searchTxt);
int pageRow = 10;
int totalcount = boardService.boardCount(map);
log.info("totalcount : "+ totalcount);
model.addAttribute("total_count", totalcount);
int total_page = totalcount / pageRow + (totalcount % pageRow > 0 ? 1 : 0);
log.info("total_page : "+ total_page);
model.addAttribute("pages", total_page);
map.put("pageNum", pageNum);
map.put("pageRow", pageRow);
List<ResultMap> boardlist = boardService.boardList(map);
model.addAttribute("pageNum", pageNum);
model.addAttribute("list", boardlist);
먼저 페이징을 하기 위해선 총 게시물수와 현재페이지 번호가 필요하다.
int total_page = totalcount / pageRow + (totalcount % pageRow > 0 ? 1 : 0);
총 게시물수를 구해와서 최대 페이지 갯수를 구한다.
필자는 게시물이 10개씩 보이게 끔 설정을 해주었다.
게시물 갯수 구하기
<select id="boardCount" parameterType="map" resultType="java.lang.Integer">
SELECT count(*) FROM 테이블명
<where>
0=0
<if test="searchTxt != ''">
<if test="searchType == 'mem_name'">
and mem_name like #{searchTxt}
</if>
<if test="searchType == 'company_name'">
and company_name like #{searchTxt}
</if>
</if>
</where>
</select>
총 게시물수는 게시판일 경우 검색어에 따른 개수가 달라지기에 검색 쿼리를 사용해 구한다.
한 가지 주의할 점은 where 절에 and 사용을 편하게 하기 위해 0=0 혹은 1=1을 사용할 수 있는데
이는 전체조회가 되는 관점에서는 매우 위험하다.
게시물 리스트 가져오기
ROW_NUMBER() 버전
<select id="boardList" parameterType="map" resultType="ResultMap">
SELECT *
FROM
(
SELECT ROW_NUMBER() OVER(ORDER BY reg_date desc) AS rownum
,* FROM 테이블명 lc
<where>
0=0
<if test="searchTxt != ''">
<if test="searchType == 'mem_name'">
and mem_name like #{searchTxt}
</if>
<if test="searchType == 'company_name'">
and company_name like #{searchTxt}
</if>
</if>
</where>
) A
WHERE rownum BETWEEN ((#{pageNum})*#{pageRow})+1 AND ((#{pageNum}+1)*#{pageRow})
</select>
OFFSET FETCH 버전
<select id="boardOftsetList" parameterType="map" resultType="ResultMap">
SELECT t1.*
FROM 테이블명 AS t1 WITH(NOLOCK)
INNER JOIN (
SELECT id
FROM 테이블명 WITH(NOLOCK)
<where>
0=0
<if test="searchTxt != ''">
<if test="searchType == 'mem_name'">
and mem_name like #{searchTxt}
</if>
<if test="searchType == 'company_name'">
and company_name like #{searchTxt}
</if>
</if>
</where>
ORDER BY reg_date DESC
OFFSET (#{pageNum}) * #{pageRow} ROWS
FETCH NEXT (#{pageRow}) ROWS ONLY
) AS t2
ON t1.id = t2.id ORDER BY reg_date DESC
</select>
보통 mssql의 경우 row_number() 이나 offset fetch를 통해 페이징 쿼리를 만든다.
offset fetch는 2012 버전부터 사용가능하며 빠르고 간편한 편이지만 특정구문 등을 사용할 수 없다.
row_number()는 모든 게시물에 번호를 달아주기 때문에 특정 범위의 검색을 할수 있지만 부하를 일으킬 수 있고 속도가 느린 편이다.
페이징 만들기
<div class="row">
<div class="col-lg-12 txt_center">
<ul class="pagination">
<fmt:parseNumber var="screenPageNum" integerOnly="true" type="number" value="${pageNum/5}" />
<fmt:parseNumber var="startScreenPageNum" integerOnly="true" type="number" value="${screenPageNum == 0 ? 1 : (screenPageNum * 5) + 1}" />
<fmt:parseNumber var="endScreenNum" integerOnly="true" type="number" value="${(screenPageNum + 1) * 5 <= pages ? (screenPageNum + 1) * 5 : pages}" />
<c:choose>
<c:when test="${screenPageNum gt 0}">
<li class="" id="p_previous_btn"><a href="#go_prevPage">«</a></li>
</c:when>
<c:otherwise>
<li class="disabled" id="p_previous_btn"><a href="javascript:void(0);">«</a></li>
</c:otherwise>
</c:choose>
<c:forEach var="i" begin="${startScreenPageNum}" end="${endScreenNum}">
<li class="<c:if test="${i-1 eq pageNum}">active</c:if>"><a href="#go_Page" data-index="${i-1}">${i}</a>
<li>
</c:forEach>
<c:choose>
<c:when test="${(screenPageNum+1)*5 ge totalPageNum}">
<li class="disabled" id="p_next_btn"><a href="javascript:void(0);">»</a></li>
</c:when>
<c:otherwise>
<li class="" id="p_next_btn"><a href="#go_nextPage">»</a></li>
</c:otherwise>
</c:choose>
</ul>
</div>
</div>
<script type="text/javascript">
$(function(){
$("a[href='#go_prevPage']").click(function(e) {
e.preventDefault();
var iPageNum = $("input[name='pageNum']").val();
var screenPageNum = parseInt(iPageNum / 5);
var ppn = (screenPageNum * 5) - 5;
$("input[name='pageNum']").val(ppn);
search(ppn);
});
$("a[href='#go_Page']").click(function(e) {
e.preventDefault();
var index = $(this).data("index");
$("input[name='pageNum']").val(index);
search(index);
});
$("a[href='#go_nextPage']").click(function(e) {
e.preventDefault();
var iPageNum = $("input[name='pageNum']").val();
var screenPageNum = parseInt(iPageNum / 5);
var npn = (screenPageNum + 1) * 5;
$("input[name='pageNum']").val(npn);
search(npn);
});
});
</script>
총 페이지 개수와 현재페이지로 시작 페이지와 끝 페이지를 구한다.
첫 페이지와 마지막 페이지는 클릭이 안되게 하고 현재 페이지는 볼드를 넣어주어 다른 페이지번호와 다르게 표시한다.
페이지를 누르면 해당페이지로 이동이 되며 다음페이지나 이전페이지를 누를 경우 페이지묶음이 5개 단위로 변경되게 된다.
페이징 CSS
<style>
.pagination {
display: inline-block;
padding-left: 0;
margin: 20px 0;
border-radius: 4px;
}
.pagination > li {
display: inline;
}
.pagination > li > a,
.pagination > li > span {
position: relative;
float: left;
padding: 6px 12px;
margin-left: -1px;
line-height: 1.42857143;
color: #428bca;
text-decoration: none;
background-color: #fff;
border: 1px solid #ddd;
}
.pagination > li:first-child > a,
.pagination > li:first-child > span {
margin-left: 0;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
.pagination > li:nth-last-child(1) > a,
.pagination > li:nth-last-child(1) > span {
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
.pagination > li > a:hover,
.pagination > li > span:hover,
.pagination > li > a:focus,
.pagination > li > span:focus {
color: #2a6496;
background-color: #eee;
border-color: #ddd;
}
.pagination > .active > a,
.pagination > .active > span,
.pagination > .active > a:hover,
.pagination > .active > span:hover,
.pagination > .active > a:focus,
.pagination > .active > span:focus {
z-index: 1;
color: #fff;
cursor: default;
background-color: #428bca;
border-color: #428bca;
}
.pagination > .disabled > span,
.pagination > .disabled > span:hover,
.pagination > .disabled > span:focus,
.pagination > .disabled > a,
.pagination > .disabled > a:hover,
.pagination > .disabled > a:focus {
color: #999;
cursor: not-allowed;
background-color: #fff;
border-color: #ddd;
}
.pagination-lg > li > a,
.pagination-lg > li > span {
padding: 10px 16px;
font-size: 18px;
}
.pagination-lg > li:first-child > a,
.pagination-lg > li:first-child > span {
border-top-left-radius: 6px;
border-bottom-left-radius: 6px;
}
.pagination-lg > li:nth-last-child(1) > a,
.pagination-lg > li:nth-last-child(1) > span {
border-top-right-radius: 6px;
border-bottom-right-radius: 6px;
}
.pagination-sm > li > a,
.pagination-sm > li > span {
padding: 5px 10px;
font-size: 12px;
}
.pagination-sm > li:first-child > a,
.pagination-sm > li:first-child > span {
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
}
.pagination-sm > li:nth-last-child(1) > a,
.pagination-sm > li:nth-last-child(1) > span {
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
}
</style>
페이징 소스는 include로 만들어 넣어줬다.
리스트에서 총 페이지와 현재페이지수만 구하고 게시판 하단에 페이징 include 파일만 넣어주면 여러 게시판에서 활용도 있게 사용할 수 있다.