기존에 paging 처리를 설명했던 포스팅도 있습니다
게시판 페이징 서버단 >> http://mkil.tistory.com/227
게시판 페이지 클라이언트단 >> http://mkil.tistory.com/228
위 포스팅은 나눠 설명이 되어져 있기 때문에 이해도가 조금 떨어질 수도 있어요.
또한, 같은 내용이긴 하지만 위 예제들은 필자가 따로 사용했던 EppltPortlet 및
DataEntitiy등 별도의 Class 및 용어들이 보이기 때문에 아마 다른 분들이 가져다가
사용하시기에는 번거로울..것 같다는 ^^; 생각이 들었습니다.
따라서 이 소스를 기본바탕으로 해서 기본적인 PagingUtil 예제를 작성해보도록 할게요..
이전 포스팅을 참조하여 환경 및 예제가 어디까지 진행 되었는지 확인해 주세요
게시판 목록 만들기 >> http://mkil.tistory.com/310
게시물 쓰기 >> http://mkil.tistory.com/313
게시물 상세보기 >> http://mkil.tistory.com/326
(1) util 패키지 생성
paging 처리를 위한 com.common.utils 패키지와 하위 PagingUtil CLASS를 생성합니다.
(2) PagingUtil 클래스 작성
생성한 PagingUtil.java를 다음과 같이 작성합니다.
자세한 설명은 http://mkil.tistory.com/227 에 해놨기 때문에 생략할께요.
package com.common.utils;
import java.util.Map;
public class PagingUtil {
private static final int countPerPage = 10;
private static final int unitPage = 10;
/** 페이지 정보 셋팅 **/
public static Map setPageInfo(Map reqParam, int defaultCountPerPage) {
int pageNo = Integer.parseInt( reqParam.get("pageNo").toString() ); //java Object -> int 형 변환
int countPerPage = Integer.parseInt( reqParam.get("countPerPage").toString() );
countPerPage = countPerPage < 100 ? countPerPage : 100; // 최대 100개로 제한
int first = ((pageNo - 1) * countPerPage) + 1, last = first + countPerPage - 1;
reqParam.put("countPerPage", countPerPage);
reqParam.put("first", first);
reqParam.put("last", last);
System.out.println("setPageInfo :::: "+reqParam);
return reqParam;
}
/** 페이징 처리 (파라미터 2개) **/
public static Map getPageObject(int totalCount, int currentPageNo)
{
return getPageObject(totalCount, currentPageNo, 10);
}
/** 페이징 처리 (파라미터 3개) **/
public static Map getPageObject(int totalCount, int currentPageNo, int countPerPage)
{
return getPageObject(totalCount, currentPageNo, countPerPage, 10);
}
/** 페이징 처리 (파라미터 4개) **/
public static Map getPageObject(int totalCount, int currentPageNo, int countPerPage, int unitPage) {
int currPage = currentPageNo;
int unitCount = 100;
boolean isFirst = false;
if (totalCount == 0) {
countPerPage = unitCount;
} else if (totalCount < countPerPage) {
countPerPage = totalCount / unitCount * unitCount;
if (totalCount % unitCount > 0) {
countPerPage += unitCount;
}
}
int totalPage = getMaxNum(totalCount, countPerPage);
if (totalPage < currPage)
currPage = totalPage;
int currStartCount;
int currEndCount;
if (currPage != 1) {
currEndCount = currPage * countPerPage;
currStartCount = currEndCount - countPerPage;
} else {
currEndCount = countPerPage;
currStartCount = 0;
}
if (currEndCount > totalCount)
currEndCount = totalCount;
int currStartPage;
int currEndPage;
if (totalPage <= unitPage) {
currEndPage = totalPage;
currStartPage = 1;
} else {
currEndPage = (currPage - 1) / unitPage * unitPage + unitPage;
currStartPage = currEndPage - unitPage + 1;
}
if (currEndPage > totalPage)
currEndPage = totalPage;
int prePage;
boolean prePage_is;
if (currStartPage != 1) {
prePage_is = true;
prePage = currStartPage - 1;
} else {
prePage_is = false;
prePage = 0;
}
int nextPage;
boolean nextPage_is;
if (currEndPage != totalPage) {
nextPage_is = true;
nextPage = currEndPage + 1;
} else {
nextPage_is = false;
nextPage = 0;
}
Map tempJSON = new java.util.HashMap();
try {
tempJSON.put("currPage", Integer.valueOf(currPage));
tempJSON.put("unitPage", Integer.valueOf(unitPage));
tempJSON.put("prePage", Integer.valueOf(prePage));
tempJSON.put("prePage_is", Boolean.valueOf(prePage_is));
tempJSON.put("nextPage", Integer.valueOf(nextPage));
tempJSON.put("nextPage_is", Boolean.valueOf(nextPage_is));
tempJSON.put("currStartPage", Integer.valueOf(currStartPage));
tempJSON.put("currEndPage", Integer.valueOf(currEndPage));
tempJSON.put("totalCount", Integer.valueOf(totalCount));
tempJSON.put("totalPage", Integer.valueOf(totalPage));
} catch (Exception localException) {
}
return tempJSON;
}
/** max 페이지 구하기 **/
private static int getMaxNum(int allPage, int list_num) {
if (allPage % list_num == 0) {
return allPage / list_num;
}
return allPage / list_num + 1;
}
}
(3) Service 수정
데이터 List를 가져오던 Service부분을 수정합니다.
기존에는 service에서 data만 가지고 왔죠?
지금은 Data를 가져올 Table의 총 개수를 가지고 온 뒤, 총 개수를 가지고
한 페이지당 출력하고 싶은 unit 개수를 사용하여
몇 개씩 data를 가지고 올 것인지 정하는 로직이 추가 되었습니다.
즉 쉽게 말하자면 데이터 총 갯수를 구해 와서 페이지의 시작값과 끝값을 계산해주는 것입니다.
그게 바로 setPageInfo 라는 거구요.
저는 한 페이지당 10개의 data를 가지고 오는 것으로 설정해 주었답니다.
setPageInfo에 대한 자세한 설명 여기에 다 있습니다.>> http://mkil.tistory.com/227
public Map selectBoardList(Map param) {
Map resultObject = new HashMap();
List result = new ArrayList();
int totalCnt = boardDao.selectListCnt(param); // DB연동_ 총 갯수 구해오기
int searchNo = 10;
int searchCntPerPage = 10;
int searchUnitPage = 10;
if (totalCnt > 0) {
PagingUtil.setPageInfo(param, 10); //param에 Page정보 파라미터 정보 put 해주기
result = boardDao.selectList(param); // 게시판 목록 data 페이징 처리 갯수만큼 가져오기
//dataList와 pageInfo 셋팅 해주고 return 하기
resultObject.put("result", result);
resultObject.put("page", PagingUtil.getPageObject(totalCnt, searchNo, searchCntPerPage, searchUnitPage ));
} else {
resultObject.put("result", result); // 빈값
resultObject.put("page", PagingUtil.getPageObject(totalCnt, 0));
}
return resultObject;
}
(4) selectListCnt의 dao 와 xml 생성
service에서 새로 생성한 selectListCnt 메소드의 dao를 다음과 같이 만들고,
public int selectListCnt(Map param) {
int result = sqlsession.selectOne("boardSql.selectListCnt",param);
return result ;
}
xml 에 count(*)를 사용하여 총 개수를 구해 옵니다.
<select id="boardSql.selectListCnt" resultType="Integer">
SELECT count(*) FROM BOARD_MAIN_TEST
</select>
(5) selectList 쿼리 수정
기존의 selectList 쿼리를 다음과 같이 수정합니다.
<resultMap id="getBoardListResult" type="HashMap">
<result property="BRD_TYPE" column="BRD_TYPE" />
<result property="BRD_CD" column="BRD_CD" />
<result property="DOCNUM" column="DOCNUM" />
<result property="ADD_USR_NM" column="ADD_USR_NM" />
<result property="TITLE" column="TITLE" />
<result property="CONTENTS" column="CONTENTS" jdbcType="CLOB" javaType="java.lang.String" />
<result property="ATTACH" column="ATTACH" />
<result property="DEL_CONF" column="DEL_CONF" />
<result property="VIEWCOUNT" column="VIEWCOUNT" />
</resultMap>
<select id="boardSql.selectList" parameterType="java.util.HashMap" resultMap="getBoardListResult">
SELECT * FROM
(SELECT ROW_NUMBER() OVER(ORDER BY DOCNUM DESC) RNUM,
BRD_TYPE,
BRD_CD,
DOCNUM,
ADD_USR_NM,
TITLE,
CONTENTS,
ATTACH,
DEL_CONF,
VIEWCOUNT
FROM BOARD_MAIN_TEST
) X WHERE X.RNUM BETWEEN #{first} AND #{last}
</select>
사실, 이 부분에서 내부 서버 오류가 나서 한참 뻘짓했습니다 (;;)
resultMap을 사용안하고 resultMap에 그냥 HashMap으로 지정해서 했을때
분명 Controller 단까지 에러없이 result data를 잘 가져오는데
화면에서 ajax 500 에러가 나더군요..........
찾아보니 CONTENTS 타입을 가져오면서 서버 내부에서 오류를 뿜는것을 확인했습니다. (에러 로그가 빨갛게 떨어지지 않아서 한참 찾았네요 까막눈...인가 @__@끙)
이유는,, Mybatis를 사용하면서 DB 데이터 타입에 CLOB, BLOB등의 타입을 selec할때는 ResultMap을 지정해줘야 했기 때문이었습니다.
예제 작성하다가 하나 배워가네요 ^^;
(6) JSON 핸들링을 위한 디펜던시 추가
jsp에서 ajax 사용을 하여 JSON을 핸들링 하기 위해 다음과 같인 디펜던시를
추가해 줍니다. jackson 라이브러리는 어떠한 형태의 데이터도 json 형태의 데이터로 자동변환을 해준답니다.
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
Controller도 수정해야 하는데, 일단 JSP단을 먼저 수정하고 나서
Controller가 수정되어야 조금은 이해하기 수월할 것 같다고 생각이 들어서
jsp부터 수정하도록 하겠습니다.
(7) ajax를 사용하여 Data 호출
Ajax에 대한 설명 및 예제는 여기를 한번 읽고 오시면 이해하기 더 쉬울수도 ^^
>> http://mkil.tistory.com/90 (아주 기초적인 부분만 설명이 되어있네요;;)
아무래도 페이징을 위해서는 화면단 부분의 작성이 중요하겠죠?
ajax는 비동기적 통신을 위한 데이터 전송 방법이기 때문에 클라이언트단과 서버단이 내부적으로 통신을 한답니다.
따라서 클라이언트의 요청에 따라 서버는 데이터를 화면의 재 로딩없이 전달해 줄 수 있죠.
기존에 작성했던 boardMain.jsp를 다음과 같이 수정합니다.
<SCRIPT LANGUAGE="JavaScript">
$(document).ready(function(){
boardMain.init();
});
var boardMain = {
init : function(){
var _this = this;
_this.btnEvent();
_this.getBoardList();
}
,btnEvent : function(){
/* 게시글 제목 클릭 상세보기 */
$('.boardTitle').on('click',function(){
var popUrl = "/spring/boardDetail?docnum="+$(this).attr('id'); //팝업창에 출력될 페이지 URL
var popOption = "width=570, height=360, resizable=no, scrollbars=no, status=no;"; //팝업창 옵션(optoin)
window.open(popUrl,"",popOption);
});
/* 작성하기 클릭 */
$('input[type=button]').on('click',function(){
var url = "/spring/boardWrite";//팝업창에 출력될 페이지 URL
location.href = url;
});
}
// ************ 3번영역 *****************
,getBoardList : function(no){
var pageNo = (no || 1);
$.ajax({
type:"GET",
url: '/spring/boardList',
dataType: "json",
data : "countPerPage="+10+"&pageNo="+pageNo,
contentType:"application/json; charset=UTF-8",
cache : false,
success : function(resData){
var item=resData.result;
var selectHtml=[];
var len=item.length;
var page=resData.page; //페이징 변수
var page_boardList = Paging(page.totalCount, 10, 10 ,pageNo, "boardList"); //공통 페이징 처리 함수 호출
//데이타 그리기
if(len >0){
$(item).each(function(i, item){
selectHtml.push('<tr>');
selectHtml.push('<th ><a href="#" >'+item.DOCNUM+'</a></th>');
selectHtml.push('<th class="boardTitle" id="'+item.DOCNUM+'"><a href="#">'+(item.TITLE || "제목없음")+'</a></th>');
selectHtml.push('<td>'+item.ADD_USR_NM+'</td>');
selectHtml.push('<td>'+item.VIEWCOUNT+'</td>');
selectHtml.push('</tr>');
});
}else{
selectHtml.push('<tr>');
selectHtml.push('<td colspan="3">조회된 결과가 없습니다.</td>');
selectHtml.push('</tr>');
}
$("#boardList").empty().html(selectHtml.join(''));
//페이징 그리기
$("#paging").empty().html(page_boardList);
},
/* ajax error 확인방법 */
error : function(request,status,error){
console.log(request);
console.log(status);
console.log(error);
}
});
}
}
//************ 4번영역 *****************
var goPaging_boardList = function(cPage){
boardMain.getBoardList(cPage); // boardAdmin 개체의 getBoardList 함수를 다시 호출
};
// ************ 2번영역 ***************** --> 목록 페이징 함수
Paging = function(totalCnt, dataSize, pageSize, pageNo, token){
totalCnt = parseInt(totalCnt); // 전체레코드수
dataSize = parseInt(dataSize); // 페이지당 보여줄 데이타수
pageSize = parseInt(pageSize); // 페이지 그룹 범위 1 2 3 5 6 7 8 9 10
pageNo = parseInt(pageNo); // 현재페이지
var html = new Array();
if(totalCnt == 0){
return "";
}
// 페이지 카운트
var pageCnt = totalCnt % dataSize;
if(pageCnt == 0){
pageCnt = parseInt(totalCnt / dataSize);
}else{
pageCnt = parseInt(totalCnt / dataSize) + 1;
}
var pRCnt = parseInt(pageNo / pageSize);
if(pageNo % pageSize == 0){
pRCnt = parseInt(pageNo / pageSize) - 1;
}
//이전 화살표
if(pageNo > pageSize){
var s2;
if(pageNo % pageSize == 0){
s2 = pageNo - pageSize;
}else{
s2 = pageNo - pageNo % pageSize;
}
html.push('<a href=javascript:goPaging_' + token + '("');
html.push(s2);
html.push('");>');
html.push('◀ ');
html.push("</a>");
}else{
html.push('<a href="#">\n');
html.push('◀ ');
html.push('</a>');
}
//paging Bar
for(var index=pRCnt * pageSize + 1;index<(pRCnt + 1)*pageSize + 1;index++){
if(index == pageNo){
html.push('<strong>');
html.push(index);
html.push('</strong>');
}else{
html.push('<a href=javascript:goPaging_' + token + '("');
html.push(index);
html.push('");>');
html.push(index);
html.push('</a>');
}
if(index == pageCnt){
break;
}else html.push(' | ');
}
//다음 화살표
if(pageCnt > (pRCnt + 1) * pageSize){
html.push('<a href=javascript:goPaging_' + token + '("');
html.push((pRCnt + 1)*pageSize+1);
html.push('");>');
html.push(' ▶');
html.push('</a>');
}else{
html.push('<a href="#">');
html.push(' ▶');
html.push('</a>');
}
return html.join("");
}
</script>
</head>
<body>
<table class="tbl_port" style="border: 1px solid #ccc">
<caption style="background-color: #ccc">목록</caption>
<colgroup>
<col width="10%" />
<col width="*"/>
<col width="15%"/>
<col width="10%"/>
</colgroup>
<thead>
<tr>
<th scope="col">글번호</th>
<th scope="col">제목</th>
<th scope="col">작성자</th>
<th scope="col">조회수</th>
</tr>
</thead>
<!-- ************* 1번영역 ***************** -->
<tbody id="boardList">
</tbody>
</table>
<br/>
<div id="paging" style="margin-left: 190px;"></div>
<br/>
<div ><input type="button" value="작성하기 " style="margin-left: 440px;"/></div>
</body>
</html>
제가 1번영역 부터 4번영역까지 주석을 달아놨어요.
각 영역별로 설명 하겠습니다.
①번 영역 설명
dataList와 pagingInfo를 같이 가져와 출력해 줄 것이기 때문에
기존의 JSTL을 사용하던 목록을 지우고 baordList 라는 id값을 추가하고
id값이 paging인 div를 추가해 주었습니다.
②번 영역 설명
Paging 이라는 page UI를 그려주는 공통 자바스크립트 함수를 작성합니다.
이 부분은 공통 js로 따로 관리해 주세요.
지금은 테스트 예제니까 같은 JSP 안에 두겠습니다 (;;)
③번 영역 설명
getBoardList라는 함수에서는 클릭한 pageNo 를 가지고 ajax를 통해
countPerPage(한 페이지당 가져올 데이터 개수) 수만큼 데이터를 가지고 옵니다.
pageNo가 1,2,3 페이지에 따라 10~20, 21~30, 31~40 이렇게 계산되어져 오는거죠.
가져온 dataList를 가지고 동적으로 table을 그려줍니다.
paging은 Paging 공통함수에 서버를 통해 가져온 page 값을 파라미터 변수로 넘겨
그려줍니다.
Paging에서 return한 html 객체를 통으로 id값이 paging인 영역에 그려주는 것이죠.
④번 영역 설명
Paging 함수 호출 시, 마지막 파라미터값에 따라 “goPaging_”+”파라미터값” 의 함수를
호출하도록 만들었습니다.
따라서 우리는 goPaging_boardList 함수를 별도로 만들어 줘야합니다.
이 함수는 page번호를 클릭할때마다 클릭한 페이지 값을 ajax를 호출하는 함수로 넘겨주는 역할을 해줍니다.
boardMain.getBoardList 는 제가 가장 상단에 임의로 정한 객체와 함수 명이기 때문에
바꾸셔서 사용하셔도 되겠죠?
(7) 결과 확인
다음과 같이 페이징 된 게시판이 나오게 됩니다.
자세히 작성해보자 하면서 했는데 오히려… 두서없이 적은 것 같네요…
이상한 부분이나, 오타, 에러를 발견하시면 댓글 달아주세요~
여기까지 jQuery를 사용한 게시판 Paging 이였습니다. ^^
'Framework > Spring' 카테고리의 다른 글
FileSystemXmlApplicationContext과 ClassPathXmlApplicationContext (1) | 2016.11.17 |
---|---|
[Spring] velocity와 spring으로 템플릿 email 보내기 (2) | 2016.11.16 |
[SPRING 시작-10]#게시판/ 게시물 상세보기 / Spring board update example/ Mybatis update (1) | 2016.10.21 |
[SPRING 시작-9]#게시판/ 게시물 쓰기 / Spring board insert example/ Mybatis insert (0) | 2016.10.18 |
[SPRING 시작-8] # 스프링 게시판을 만들어 보자! / 게시판 목록 소스 / Spring board example (1) | 2016.10.18 |