현재의 리스트는 전체 데이터를 출력한다.
이것이 실제 사용될 경우 많은 데이터로 인해 문제를 발생시킬 수 있다.
즉, 예제는 몇 건 내지 몇 십 건의 데이터를 처리하기 때문에 문제가 없지만
실제 사용될 경우 몇 만에서 몇 십만의 데이터가 저장되기 때문에 한 페이지 실행되는데 아주 오랜 시간이 걸리고
웹 브라우저가 실행할 때도 메모리 부족 등의 문제가 발생할 수 있다.
따라서, 필요한 데이터만 가져오는 방식으로 처리해야 한다.
데이터가 얼마가 저장되어 있든지 필요로 하는 개수(화면에 출력하고자 하는 개수)만큼 가져오면
데이터 양과 관계없이 일정한 속도를 유지하게 된다.
이러한 처리를 페이징(Paging) 처리하고 한다.
이 부분은 많은 개발자들이 자주 실수하는 부분이며 웹 사이트 성능 저하의 주범이기도 하다.
다음과 같이 전체 데이터 개수가 9개가 있고 웹 페이지에는 3개씩 보여 준다고 가정한다.
1 … … …
2 … … …
3 … … …
4 … … …
5 … … …
6 … … …
7 … … …
8 … … …
9 … … …
전체 페이지 수는 9 / 3으로 3이다.
첫 페이지의 데이터는 레코드 번호가 1부터 3까지
두 번째 페이지의 데이터는 레코드 번호가 4부터 6까지
세 번째 페이지의 데이터는 레코드 번호가 7부터 9가 된다.
여기에서 공식이 나오게 된다.
현재 페이지에 보여줄 데이터의 시작 번호 = (현재 페이지 – 1) * 출력 개수 + 1
현재 페이지에 보여줄 데이터의 종료 번호 = 시작 번호 + 출력개수 - 1
위 공식에 실제 페이지를 대입해 보면
1 페이지의 시작 페이지는 = (1-1)* 3 + 1 = 1, 종료 페이지는 1 + 3 – 1 = 3
2 페이지의 시작 페이지는 = (2-1)* 3 + 1 = 4, 종료 페이지는 4 + 3 – 1 = 6
3 페이지의 시작 페이지는 = (3-1)* 3 + 1 = 7, 종료 페이지는 7 + 3 – 1 = 9
따라서, 다음과 같이 SQL 문이 실행되도록 만들면 되는 것이다.
MariaDB의 Limit는 주어진 레코드 번호 값에 해당하는 데이터를 가지고 오고, Oracle이나 MS-SQL은 Row_number나 TOP등의 명령어가 제공된다.
SELECT BRDNO, BRDTITLE, ~~ 생략 ~~
FROM TBL_BOARD
WHERE BRDDELETEFLAG='N'
ORDER BY BRDNO DESC
LIMIT 1, 3
SELECT BRDNO, BRDTITLE, ~~ 생략 ~~
FROM TBL_BOARD
WHERE BRDDELETEFLAG='N'
ORDER BY BRDNO DESC
LIMIT 4, 6
SELECT BRDNO, BRDTITLE, ~~ 생략 ~~
FROM TBL_BOARD
WHERE BRDDELETEFLAG='N'
ORDER BY BRDNO DESC
LIMIT 7, 9
이렇게 데이터를 가지고 오고,
리스트의 하단에 전체 페이지 리스트를 보여 주어 사용자가 선택하도록 하면 된다.
페이지 리스트는 전체 데이터 수(9)를 구해서
보여주고자 하는 개수(3)로 나누어 전체 페이지 수(3)를 구하고
그 수만큼 반복해서 링크를 생성한다.
이상의 개념을 이해한 경우 직접 만들어서 사용하면 되고,
그렇지 못해도 문제는 없다.
Common 폴더에 PageVO 클래스에 만들어 두었기 때문에 가져다 사용하면 된다.
public class PageVO {
private Integer displayRowCount=10; // 출력할 데이터 개수
private Integer rowStart, rowEnd; // 시작행번호, 종료행 번호
private Integer totPage, totRow=0, // 전체 페이수, 전체 데이터 수
page, pageStart, pageEnd; // 현재 페이지, 시작페이지, 종료페이지
public void pageCalculate(Integer total) {
getPage();
totRow = total;
totPage = (int) ( total / displayRowCount );
if ( total % displayRowCount > 0 ) totPage++;
pageStart = (page - (page - 1) % 10) ;
pageEnd = pageStart + 9;
if (pageEnd > totPage) pageEnd = totPage;
rowStart = ((page - 1) * displayRowCount) + 1 ;
rowEnd = rowStart + displayRowCount -1;
}
~~ 생략 ~~
이 코드를 간단하게 설명하면 전체 데이터 개수(total)를 받아서 totRow에 보관한다.
전체 데이터수를 출력하고자 하는 개수(displayRowCount)로 나누어
전체 페이지(totPage)를 계산한다.
무조건 올림을 해야 정확한 페이지 수를 얻을 수 있다.
예로 데이터 수가 21 개 일 때 단순 나누기를 하면 2페이지가 나온다.
무조건 올림을 해야 정확한 3페이지를 얻을 수 있다.
그래서 나눈 후 나머지가 있으면 전체 페이지(totPage) 값을 1증가 시킨다.
시작 행 번호(rowStart)와 종료 행 번호(rowEnd)는 앞서 언급한 공식을 이용하여 생성하였다.
시작페이지(pageStart), 종료페이지(pageEnd)는 설명하지 않는다.
다만, 데이터가 수천, 수 만 일 경우 페이지도 수백, 수천이 된다.
모든 페이지를 보여 줄 수 없기 때문에
페이지 리스트에 대한 페이징 처리를 하게 되고 이것을 의미한다.
원리는 같지만 이상의 것만으로 복잡해서 넘어간다.
차후 코드를 살펴 보기 바란다.
먼저, 컨트롤을 수정한다.
@RequestMapping(value = "/board2List")
public String boardList(PageVO pageVO, ModelMap modelMap) throws Exception {
pageVO.pageCalculate( boardSvc.selectBoardCount() ); // startRow, endRow
List<?> listview = boardSvc.selectBoardList(pageVO);
modelMap.addAttribute("listview", listview);
modelMap.addAttribute("pageVO", pageVO);
return "board2/boardList";
}
board2Ctr.java
board2List의 파라메터로 PageVO 클래스 값을 받아오는 것은
사용자가 선택한 페이지 값을 알기 위한 것이다.
값이 없는 경우(Null) 1 페이지로 처리한다.
앞서 언급한 데로 PageVO에 메소드로 pageCalculate가 정의 되어 있기 때문에
서비스에서 전체 데이터 개수(COUNT)를 구하는 SQL문을 실행한다.
<select id="selectBoard2Count" resultType="Integer" >
SELECT COUNT(*)
FROM TBL_BOARD
WHERE BRDDELETEFLAG='N'
</select>
Board2.xml
public Integer selectBoardCount() throws Exception {
return sqlSession.selectOne("selectBoard2Count");
}
board2Svc.java
pageVO에서 시작 행 번호(rowStart)와 종료 행 번호(rowEnd)를 계산해서(pageCalculate) 가지고 있기 때문에,
selectBoardList를 호출 할때 파라메터로 넘겨준다.
이 값들은 다음과 같이 사용된다.
<select id="selectBoard2List" resultType="gu.board2.boardVO" parameterType="gu.common.PageVO">
SELECT BRDNO, BRDTITLE, BRDWRITER, DATE_FORMAT(BRDDATE,'%Y-%m-%d') BRDDATE, BRDHIT
FROM TBL_BOARD
WHERE BRDDELETEFLAG='N'
ORDER BY BRDNO DESC
LIMIT ${rowStart-1}, 10 (※주의)
</select>
board2.xml
public List<?> selectBoardList(PageVO param) throws Exception {
return sqlSession.selectList("selectBoard2List", param);
}
board2Svc.java
이상의 처리로 페이징 처리는 끝났다.
10개 이상의 데이터를 넣고 실행해보면 10 개만 출력되는 것을 알 수 있다.
※ 주의: 앞서 필요한 데이터를 가져오는 부분에 대한 설명에 있어서 LIMIT 1, 3 ~~ LIMIT 4, 6 ~~ LIMIT 7, 9 는
첫 번째부터 세 번째, 네 번째부터 여섯 번째 등으로 설명하기 하기 위한 것이고 MariaDB(Mysql)의 LIMIT 문은
첫 번째부터 세 개, 네 번째부터 세 개로 사용해야 한다. 따라서
LIMIT 1, 3 ~~ LIMIT 4, 3 ~~ LIMIT 7, 3 으로 사용해야 하고
앞서의 Mybatis SQL문(board2.xml)에서는 그렇게 사용하고 있다. Oracle이나 MS-SQL에서 Row_number를 사용할 경우에는
앞서의 설명대로 사용하여 LIMIT ${rowStart-1}, ${rowEnd}로 표현해서 사용한다.
|
이제 JSP에서 리스트 하단에 페이지 번호들이 나오게 처리해야 하다.
시작페이지(pageStart)부터 종료페이지(pageEnd)까지 반복하면서(forEach)하면 그 값을 링크를 걸어서 출력(out)하면 된다.
</c:forEach>
</tbody>
</table>
<c:if test="${pageVO.totPage>1}">
<div class="paging">
<c:forEach var="i" begin="${pageVO.pageStart}" end="${pageVO.pageEnd}" step="1">
<c:url var="pageLink" value="board2List">
<c:param name="page" value="${i}" />
</c:url>
<a href="${pageLink}"><c:out value="${i}"/></a>
</c:forEach>
</div>
</c:if>
boardList.jsp
이 코드에서 현재 페이지(paveVO.page)는 링크를 걸지 않게 만들면 더 좋을 것이니 직접 해 보길 바란다.
게시물들은 등록된 후 수정되고 삭제 된다.
삭제 된 경우 리스트에서 보여 주는 글 번호가 들쑥날쭉 보이면서 뭔가 문제가 있는 것처럼 보인다.
아래의 계산식을 사용하면 깨끗하게 이어진 새로운 번호가 보이게 된다.
다음 코드를 사용해 보고 계산식은 각자 확인해 보길 바란다.
<tr>
<td>
<c:out value="${pageVO.totRow-((pageVO.page-1)*pageVO.displayRowCount + status.index)}"/>
</td>
<td><a href="${link}"><c:out value="${listview.brdtitle}"/></a></td>
<td><c:out value="${listview.brdwriter}"/></td>
<td><c:out value="${listview.brddate}"/></td>
<td><c:out value="${listview.brdhit}"/></td>
</tr>
boardList.jsp