이전 글들에서 방명록 애플리케이션을 이해하셨다면, 충분히 게시글의 CRUD , 댓글 , 검색 등의 기능이 있는 게시판도 만들 수 있습니다.

이번 글에서는 게시판에서 페이징( paging )을 하는 알고리즘을 소개하려고 합니다.




1. 페이징

페이징이란 게시판을 page 단위로 나누는 행위를 의미합니다.



페이징은 자신이 직접 구현해보는 것이 중요합니다.

페이징은 어느 게시판에서도 적용되는 알고리즘이기 때문에 남의 코드를 가져다 쓰면 매 번 페이징을 할 때 마다 남의 코드를 가져다 써야 합니다.

아무리 생각해도 구현이 어려울 때 다른 사람의 코드를 참고하는 것은 좋습니다.

중요한 것은 매 번 똑같은 페이징 알고리즘을 자신의 것으로 만드는 것이라 생각합니다.



우선 페이징에 필요한 것이 무엇인지 알아야 합니다.

  • 몇 개의 게시글로 page를 나눌 것인가
  • 게시글 수 만큼 page 번호가 존재할 것인가, 아니면 고정된 page 번호를 보여줄 것인가
    • 고정된 page일 경우 게시글이 없는 page는 어떻게 처리할 것인가
  • ...
  • ...

이런 식으로 페이징을 어떻게 할 지 정리해두는 것이 좋습니다.





지금부터 살펴 볼 코드는 위와 같은 구조가 됩니다.



2. Paging 클래스

public class Paging {
private final static int pageCount = 5;
private int blockStartNum = 0;
private int blockLastNum = 0;
private int lastPageNum = 0;

public int getBlockStartNum() {
return blockStartNum;
}
public void setBlockStartNum(int blockStartNum) {
this.blockStartNum = blockStartNum;
}
public int getBlockLastNum() {
return blockLastNum;
}
public void setBlockLastNum(int blockLastNum) {
this.blockLastNum = blockLastNum;
}
public int getLastPageNum() {
return lastPageNum;
}
public void setLastPageNum(int lastPageNum) {
this.lastPageNum = lastPageNum;
}

// block을 생성
// 현재 페이지가 속한 block의 시작 번호, 끝 번호를 계산
public void makeBlock(int curPage){
int blockNum = 0;

blockNum = (int)Math.floor((curPage-1)/ pageCount);
blockStartNum = (pageCount * blockNum) + 1;
blockLastNum = blockStartNum + (pageCount-1);
}

// 총 페이지의 마지막 번호
public void makeLastPageNum() {
BoardDAO dao = new BoardDAO();
int total = dao.getCount();

if( total % pageCount == 0 ) {
lastPageNum = (int)Math.floor(total/pageCount);
}
else {
lastPageNum = (int)Math.floor(total/pageCount) + 1;
}
}

// 검색을 했을 때 총 페이지의 마지막 번호
public void makeLastPageNum(String kwd) {
BoardDAO dao = new BoardDAO();
int total = dao.getCount(kwd);

if( total % pageCount == 0 ) {
lastPageNum = (int)Math.floor(total/pageCount);
}
else {
lastPageNum = (int)Math.floor(total/pageCount) + 1;
}
}
}
  • 여기서 block이란 1 ~ 5 page가 속해있는 그룹을 의미합니다.
    • 6 ~ 10 page는 1 ~ 5 page와 다른 그룹이 되는 것이죠.
  • 그 block의 첫 번째 page 번호를 blockStartNum 이라는 변수에, 마지막 page 번호를 blockLastNum 이라는 변수에 담을 것입니다.
    • 예를 들어, 현재 block의 페이지가 6 ~ 10 page라면, blockStartNum = 6 , blockLastNum = 10이 됩니다
  • lastPageNum 변수는 전체 page를 통틀어서 마지막 page 번호를 담는 변수입니다.
    • 아래 사진에서 lastPageNum = 6일 때,  7, 8, 9, 10는 링크를 활성화 하지 못하도록 하기 위해 필요한 변수입니다.

  • dao.getCount() 메서드는 BoardDAO에서 게시글의 개수를 가져오는 쿼리를 수행하는 메서드입니다.
    • kwd는 키워드를 의미하며, 오버로딩한 이유는 검색을 할 때 검색된 게시글의 개수를 가져오기 위함입니다.



컨트롤러는 Paging 객체에 현재 페이지( curPage )번호를 넘겨준 후

변수를 가져오는 getter를 불러와서 jsp에 전달하기만 하면 됩니다.

Integer blockStartNum = paging.getBlockStartNum();
Integer blockLastNum = paging.getBlockLastNum();
Integer lastPageNum = paging.getLastPageNum();



이제 jsp에서 page 부분을 출력해보겠습니다.

<div class="pager">
<ul>
<c:if test="${ curPageNum > 5 && !empty kwd }">
<li><a href="/mysite/board?page=${ blockStartNum - 1 }&kwd=${ kwd }">◀</a></li>
</c:if>

<c:if test="${ curPageNum > 5 }">
<li><a href="/mysite/board?page=${ blockStartNum - 1 }">◀</a></li>
</c:if>

<c:forEach var="i" begin="${ blockStartNum }" end="${ blockLastNum }">
<c:choose>
<c:when test="${ i > lastPageNum }">
<li>${ i }</li>
</c:when>
<c:when test="${ i == curPageNum }">
<li class="selected">${ i }</li>
</c:when>
<c:when test="${ !empty kwd}">
<li><a href="/mysite/board?a=search&page=${ i }&kwd=${ kwd }">${ i }</a></li>
</c:when>
<c:otherwise>
<li><a href="/mysite/board?page=${ i }">${ i }</a></li>
</c:otherwise>
</c:choose>
</c:forEach>

<c:if test="${ lastPageNum > blockLastNum && !empty kwd }">
<li><a href="/mysite/board?a=search&page=${ blockLastNum + 1 }&kwd=${ kwd }">▶</a></li>
</c:if>

<c:if test="${ lastPageNum > blockLastNum }">
<li><a href="/mysite/board?page=${ blockLastNum + 1 }">▶</a></li>
</c:if>
</ul>
</div>

JSTL 문법이 있어서 조금 복잡해 보일 수 있습니다.

  • 는 키워드가 있느냐, 즉 검색에 대한 게시글 리스트인지 아닌지에 따라 분기를 나눴습니다.
  • 그리고 1,2,3,4,5 와 같은 page 번호는 반복문을 돌면서 출력하도록 했는데, 조건을 나눠서 page 번호를 출력하고 있습니다.
    1. 게시글이 없는 page 번호인지
      • page 번호만 있고 링크 비활성화 
    2. 현재 선택한 page 번호인지
      • css 속성으로 빨간색으로 표시
    3. 검색이 아닌 게시글 리스트인지
      • URL 파라미터에 kwd가 없는 링크 작성
    4. 그 외


a태그의 URL을 보시면 아시겠지만 pagekwd는 URL 파라미터로 전달이 되고 있습니다.

이런 식으로 현재 page 번호와 검색 키워드를 컨트롤러에 전달시키고 이에 따라 게시글 리스트를 불러옵니다.




이상으로 paging 알고리즘을 구현해보았습니다.

그런데 사실 전체 코드도 없고, 무슨 말인지 이해가 안되실 것 같습니다.

저도 Paging을 처음 할 때 구글링을 하면서 다른 사람의 코드를 보다가 이해가 안되서 그냥 제가 구현했거든요...

그런데 다른 사람의 코드를 보면서 아이디어를 얻긴 했었습니다.

이 글을 보시면서 아이디어를 얻어가셨으면 좋겠습니다.