티스토리 뷰


안녕하세요 강정호입니다. 오늘은 제가 푸드트럭 서비스의 게시판을 제작할 때 페이징 처리를 어떻게 하였는지에 대해서 다루어보겠습니다.



페이징 처리


페이징 처리는 게시물이 많을 때 한 페이지에 보여지는 게시물의 수를 정하고 나머지는 페이지 번호로 처리하는 것을 말합니다. 


페이징 처리에 있어서 핵심적인 정보 2가지가 있습니다.

1) 한 페이지에 보여줄 게시물의 갯수

2) 총 게시물의 갯수


그럼 본격적으로 페이징 처리 코드를 살펴보겠습니다.



PagingBean 코드


package org.asechs.wheelwego.model.vo;

/**
 * 페이징 처리를 위한 비즈니스 계층의 클래스 PagingBean method 구현순서 getStartRowNumber()
 * getEndRowNumber() getTotalPage() getTotalPageGroup() getNowPageGroup()
 * getStartPageOfPageGroup() getEndPageOfPageGroup() isPreviousPageGroup()
 * isNextPageGroup()
 * 
 * @author kosta
 *
 */
public class PagingBean {
/**
* 현재 페이지
*/
private int nowPage = 1;
public int getPageNumberPerPageGroup() {
return pageNumberPerPageGroup;
}

public void setPageNumberPerPageGroup(int pageNumberPerPageGroup) {
this.pageNumberPerPageGroup = pageNumberPerPageGroup;
}

public int getTotalContents() {
return totalContents;
}

public void setTotalContents(int totalContents) {
this.totalContents = totalContents;
}

public void setNowPage(int nowPage) {
this.nowPage = nowPage;
}

/**
* 페이지당 게시물수
*/
private int contentNumberPerPage = 8;
/**
* 페이지 그룹당 페이지수
*/
private int pageNumberPerPageGroup = 5;
/**
* database에 저장된 총게시물수
*/
private int totalContents;

/**
* 페이지 검색 단어
*/
private String searchWord;
/**
* 트럭 GPS 정보
*/
private TruckVO gpsInfo;
private String customerId;
/**
* 예약 내역 가져올 때 쓸 foodTruck_number
*/
private String foodTruckNumber;

public String getCustomerId() {
return customerId;
}

public void setCustomerId(String customerId) {
this.customerId = customerId;
}

public PagingBean() {
}

public int getContentNumberPerPage() {
return contentNumberPerPage;
}

public void setContentNumberPerPage(int contentNumberPerPage) {
this.contentNumberPerPage = contentNumberPerPage;
}

public PagingBean(int totalContents) {
this.totalContents = totalContents;
}

public PagingBean(int totalContents, int nowPage) {
this.totalContents = totalContents;
this.nowPage = nowPage;
}

public PagingBean(int nowPage, int totalContents, String searchWord) {
super();
this.nowPage = nowPage;
this.totalContents = totalContents;
this.searchWord = searchWord;
}

public int getNowPage() {
return nowPage;
}

/**
* 현재 페이지번호에 해당하는 시작 게시물의 row number를 반환 hint : 이전페이지의 마지막 번호 + 1 ((현재페이지-1)
* * 페이지당 게시물수) +1
* @return
*/
public int getStartRowNumber() {
return ((nowPage - 1) * contentNumberPerPage) + 1;
}

/**
* 현재 페이지에서 보여줄 게시물 행(row)의 마지막 번호 현재페이지*contentNumberPerPage 만약 총게시물수보다<br>
* 연산결과의 번호가 클 경우 총게시물수가 마지막 번호가 되어야 한다 ex) 총게시물수 7 개 총페이지는 2페이지 : 1 2 3 4
* 5<br>
* | 6 7 | 1page 2page 현재페이지는 2페이지이고 2*5(페이지당 게시물수) 는 10 이고 실제 마지막 번호 7이다
* -><br>
* 연산결과가 총게시물수보다 클 경우 총게시물수가 마지막번호가 되어야 함
* @return
*/
public int getEndRowNumber() {
int endRowNumber = nowPage * contentNumberPerPage;
if (totalContents < endRowNumber)
endRowNumber = totalContents;
return endRowNumber;
}

/**
* 총 페이지 수를 return한다.<br>
* 1. 전체 데이터(게시물) % 한 페이지에 보여줄 데이터 개수 <br>
* => 0 이면 둘을 / 값이 총 페이지 수<br>
* 2. 전체 데이터(게시물) % 한 페이지에 보여줄 데이터 개수 <br>
* => 0보다 크면 둘을 / 값에 +1을 한 값이 총 페이지 수<br>
* 게시물수 1 2 3 4 5 6 7 8 9 10 11 12<br>
* 1페이지 1~5<br>
* 2페이지 6~10<br>
* 3페이지 11 <br>
* ex) 게시물 32 개 , 페이지당 게시물수 5개-> 7 페이지
* @return
*/
public int getTotalPage() {
int num = this.totalContents % this.contentNumberPerPage;

int totalPage = 0;
if (num == 0) {
totalPage = this.totalContents / this.contentNumberPerPage;
} else {
totalPage = this.totalContents / this.contentNumberPerPage + 1;
}
return totalPage;
}

/**
* 총 페이지 그룹의 수를 return한다.<br>
* 1. 총 페이지수 % Page Group 내 Page 수. <br>
* => 0 이면 둘을 / 값이 총 페이지 수<br>
* 2. 총 페이지수 % Page Group 내 Page 수. <br>
* => 0보다 크면 둘을 / 값에 +1을 한 값이 총 페이지 수<br>
* ex) 총 게시물 수 23 개 <br>
* 총 페이지 ? 총 페이지 그룹수 ? <br>
* 페이지 1 2 3 4 5<br>
* 페이지그룹 1234(1그룹) 5(2그룹)<br>
*/
public int getTotalPageGroup() {
int num = this.getTotalPage() % this.pageNumberPerPageGroup;
int totalPageGroup = 0;
if (num == 0) {
totalPageGroup = this.getTotalPage() / this.pageNumberPerPageGroup;
} else {
totalPageGroup = this.getTotalPage() / this.pageNumberPerPageGroup + 1;
}
return totalPageGroup;
}

/**
* 현재 페이지가 속한 페이지 그룹 번호(몇 번째 페이지 그룹인지) 을 return 하는 메소드 <br>
* 1. 현재 페이지 % Page Group 내 Page 수 => 0 이면 <br>
* 둘을 / 값이 현재 페이지 그룹. <br>
* 2. 현재 페이지 % Page Group 내 Page 수 => 0 크면 <br>
* 둘을 / 값에 +1을 한 값이 현재 페이지 그룹<br>
* 페이지 1 2 3 4 /5 6 7 8/ 9 10 1그룹 2그룹 3그룹
* @return
*/
public int getNowPageGroup() {
int num = this.nowPage % this.pageNumberPerPageGroup;
int nowPageGroup = 0;
if (num == 0) {
nowPageGroup = this.nowPage / this.pageNumberPerPageGroup;
} else {
nowPageGroup = this.nowPage / this.pageNumberPerPageGroup + 1;
}
return nowPageGroup;
}

/**
* 현재 페이지가 속한 페이지 그룹의 시작 페이지 번호를 return 한다.<br>
* Page Group 내 Page 수*(현재 페이지 그룹 -1) + 1을 한 값이 첫 페이지이다.<br>
* (페이지 그룹*페이지 그룹 개수, 그룹의 마지막 번호이므로) <br>
* 페이지 그룹 <br>
* 1 2 3 4 -> 5 6 7 8 -> 9 10 <br>
* @return
*/
public int getStartPageOfPageGroup() {
int num = this.pageNumberPerPageGroup * (this.getNowPageGroup() - 1) + 1;
return num;
}

/**
* 현재 페이지가 속한 페이지 그룹의 마지막 페이지 번호를 return 한다.<br>
* 1. 현재 페이지 그룹 * 페이지 그룹 개수 가 마지막 번호이다. <br>
* 2. 그 그룹의 마지막 페이지 번호가 전체 페이지의 마지막 페이지 번호보다 <br>
* 큰 경우는 전체 페이지의 마지막 번호를 return 한다.<br>
* 1 2 3 4 -> 5 6 7 8 -> 9 10
* @return
*/
public int getEndPageOfPageGroup() {
int num = this.getNowPageGroup() * this.pageNumberPerPageGroup;
if (this.getTotalPage() < num) {
num = this.getTotalPage();
}
return num;
}

/**
* 이전 페이지 그룹이 있는지 체크하는 메서드 <br>
* 현재 페이지가 속한 페이지 그룹이 1보다 크면 true<br>
* ex ) 페이지 1 2 3 4 / 5 6 7 8 / 9 10 <br>
* 1 2 3 group
* @return
*/
public boolean isPreviousPageGroup() {
boolean flag = false;
if (this.getNowPageGroup() > 1) {
flag = true;
}
return flag;
}

/**
* 다음 페이지 그룹이 있는지 체크하는 메서드 <br>
* 현재 페이지 그룹이 마지막 페이지 그룹(<br>
* 마지막 페이지 그룹 == 총 페이지 그룹 수) 보다 작으면 true<br>
* * ex ) 페이지 <br>
* 1 2 3 4 / 5 6 7 8 / 9 10 <br>
* 1 2 3 group
* @return
*/
public boolean isNextPageGroup() {
boolean flag = false;
if (this.getNowPageGroup() < this.getTotalPageGroup()) {
flag = true;
}
return flag;
}

public String getSearchWord() {
return searchWord;
}

public void setSearchWord(String searchWord) {
this.searchWord = searchWord;
}

public TruckVO getGpsInfo() {
return gpsInfo;
}

public void setGpsInfo(TruckVO gpsInfo) {
this.gpsInfo = gpsInfo;
}

public String getFoodTruckNumber() {
return foodTruckNumber;
}

public void setFoodTruckNumber(String foodTruckNumber) {
this.foodTruckNumber = foodTruckNumber;
}

@Override
public String toString() {
return "PagingBean [nowPage=" + nowPage + ", contentNumberPerPage=" + contentNumberPerPage
+ ", pageNumberPerPageGroup=" + pageNumberPerPageGroup + ", totalContents=" + totalContents
+ ", searchWord=" + searchWord + ", gpsInfo=" + gpsInfo + ", customerId=" + customerId
+ ", foodTruckNumber=" + foodTruckNumber + "]";
}
}


위의 코드가 페이징빈의 전체 코드입니다. 필요하시면 사용하셔도 됩니다. 그럼 코드를 잘라서 설명드리겠습니다.


페이징 빈의 변수

1) private int nowPage : 현재 위치한 페이지의 번호를 뜻합니다. 맨 처음 게시판에 접속했을 때는 페이지 이동이 없기 때문에 디폴트로 1로 설정합니다.


2) private int contentNumberPerPage : 한 페이지당 보여줄 게시물의 수


3) private int pageNumberPerPageGroup : 페이지 그룹당 페이지수. 게시판에 보면 하단에 페이지 번호가 1 2 3 4 5 이런 식으로 표시됩니다. 만약에 5로 설정하면 1 2 3 4 5 5개가 1개의 페이지 그룹이 되는 것입니다. 8로 설정하게 되면 1 2 3 4 5 6 7 8 총 8개가 1개의 페이지 그룹이 되는 것입니다.


4) private int totalContents : 전체 게시물의 총 개수.




페이징을 위한 ListVO 객체 생성


게시물을 페이징 하기 위해서는 게시물 리스트와 PagingBean 객체를 묶어주는 객체가 필요합니다. 저는 그것을 ListVO라고 부르겠습니다. 그래서 ListVO 안에는 멤버 변수로 PagingBean과 게시물 리스트를 가지고 있어야합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ListVO {
    private List<BoardVO> boardList;
    private List<TruckVO> truckList;
    private List<ReviewVO> reviewList;
    private List<BookingVO> bookingNumberList;
    private List<BookingVO> bookingMenuList;
    private PagingBean pagingBean;
    private List<PointVO> pointList;
    public ListVO() {
        super();
        // TODO Auto-generated constructor stub
    }
 
    public ListVO(List<BoardVO> boardList, PagingBean pagingBean) {
        super();
        this.boardList = boardList;
        this.pagingBean = pagingBean;
    }
    
    public ListVO(PagingBean pagingBean, List<TruckVO> truckList) {
        super();
        this.truckList = truckList;
        this.pagingBean = pagingBean;
    }
cs



보시는것과 같이 BoardVO, TruckVO, ReviewVO 등과 같이 페이징 처리할 리스트 객체들을 멤버변수로 가지고 있습니다. 그리고 PagingBean을 멤버 변수로 가지고 있습니다.


그리고 그 밑에 보시면 ListVO의 생성자가 여러개로 오버로딩 된 것을 보실 수 있습니다.

저는 BoardVO에 대한 페이징 처리를 보여드리겠습니다.



Service 레이어에서 게시물 페이징 처리


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
     * 강정호
     * 2017.06.21(수정완료)
     * 게시판 - 자유게시판 게시물 목록 보기
     * -------------------------------------------
     * 코드 설명 : 자유 게시판의 게시물 목록을 페이지 번호와 함께 보여준다.
     * 사용자가 처음 게시판에 접속 할 때는 페이지 번호는 null로 받아오고
     * 페이지 번호가 2,3,4 등 null이 아닐 경우로 케이스를 나누어 페이징 빈 객체를 활용한다.
     * 페이징 빈 객체에는 총 게시물 수와 페이지 번호를 넣어 준 후 DAO에 보낸다.
     */
    @Override
    public ListVO getFreeBoardList(String pageNo) {
        int totalCount = boardDAO.getFreeBoardTotalContentCount();
        PagingBean pagingBean = null;
        if (pageNo == null)
            pagingBean = new PagingBean(totalCount, 1);
        else
            pagingBean = new PagingBean(totalCount, Integer.parseInt(pageNo));
        return new ListVO((List<BoardVO>) boardDAO.getFreeBoardList(pagingBean), pagingBean);
    }
cs


1) totalCount : 게시물의 총 개수 정보를 가져옵니다. DAO의 getFreeBoardTotalContentCount() 메서드를 호출하여 자유게시판의 총 게시물 개수를 가져옵니다.


2) PagingBean 타입의 변수를 선언합니다.


3) 페이지 번호에 따라서 페이징 빈을 생성합니다. 최초에 자유게시판에 접속하게 되면 페이지 번호가 없기 때문에 1로 정해서 PagingBean 객체를 생성합니다. 하지만 pageNo가 있다면 그 페이지 번호로 페이징빈 객체를 생성합니다.


4) new ListVO((List<BoardVO>) boardDAO.getFreeBoardList(pagingBean), pagingBean) : 자유게시판의 게시물과 페이징빈 객체를 넣은 ListVO를 생성합니다.


여기서 boardDAO.getFreeBoardList(pagingBean) 메서드에서 왜 pagingBean이 매개변수로 들어가 있는지 궁금해 하실 수 있습니다.


getFreeBoardList(pagingBean) 메서드에 대해서 알아보겠습니다.

1
2
3
4
5
6
7
8
9
<select id="getFreeBoardList" parameterType="pagingBean" resultMap="freeBoardListRM" resultType="boardVO">
        <!-- select freeboard_no, id, freeboard_title, freeboard_content, freeboard_timePosted, freeboard_hits
         from freeboard -->
        SELECT f.freeboard_no, f.id, f.freeboard_title, f.freeboard_content, f.freeboard_timePosted, f.freeboard_hits, m.id FROM(
        SELECT row_number() over(order by freeboard_no desc) as rnum, freeboard_no, id, freeboard_title, freeboard_content,
        to_char(freeboard_timePosted,'YYYY.MM.DD') as freeboard_timePosted , freeboard_hits from freeboard
        )f, member m where f.id=m.id and rnum between #{startRowNumber} and #{endRowNumber}
        order by freeboard_no desc
    </select>
cs


DAO에서 DB로 호출하는 getFreeBoardList 함수를 MyBatis xml 파일에 쿼리로 작성한 것입니다. 보시면 between #{startRowNumber} and #{endRowNumber}로 작성되어 있습니다. 즉 일정 게시물의 개수를 가져오기 위해 만든 쿼리입니다. 이렇게 해서 일정 개수만큼 게시물을 반환 받을 수 있습니다.




View에서의 페이징 코드


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<class="paging" align="center">
    <%-- 코드를 줄이기 위해 pb 변수에 pagingBean을 담는다. --%>
    <c:set var="pb" value="${requestScope.freeBoardList.pagingBean}"></c:set>
    <!-- 
            step2 1) 이전 페이지 그룹이 있으면 이미지 보여준다. (img/left_arrow_btn.gif)
                           페이징빈의 previousPageGroup 이용 
                   2)  이미지에 이전 그룹의 마지막 페이지번호를 링크한다. 
                           hint)   startPageOfPageGroup-1 하면 됨          
     -->      
    <c:if test="${pb.previousPageGroup}">
    <a href="${pageContext.request.contextPath}/freeboard_list.do?pageNo=${pb.startPageOfPageGroup-1}">
    <!-- <img src="img/left_arrow_btn.gif"> -->
    ◀&nbsp; </a>    
    </c:if>
    <!-- step1. 1)현 페이지 그룹의 startPage부터 endPage까지 forEach 를 이용해 출력한다
                   2) 현 페이지가 아니면 링크를 걸어서 서버에 요청할 수 있도록 한다.
                      현 페이지이면 링크를 처리하지 않는다.  
                      PagingBean의 nowPage
                      jstl choose 를 이용  
                      예) <a href="list.do?pageNo=...">                   
     -->        
    <c:forEach var="i" begin="${pb.startPageOfPageGroup}" 
    end="${pb.endPageOfPageGroup}">
    <c:choose>
    <c:when test="${pb.nowPage!=i}">
    <a href="${pageContext.request.contextPath}/freeboard_list.do?pageNo=${i}">${i}</a> 
    </c:when>
    <c:otherwise>
    ${i}
    </c:otherwise>
    </c:choose>
    &nbsp;
    </c:forEach>     
    <!-- 
            step3 1) 다음 페이지 그룹이 있으면 이미지(img/right_arrow_btn.gif) 보여준다. 
                           페이징빈의 nextPageGroup 이용 
                   2)  이미지에 이전 그룹의 마지막 페이지번호를 링크한다. 
                           hint)   endPageOfPageGroup+1 하면 됨          
     -->   
    <c:if test="${pb.nextPageGroup}">
    <a href="${pageContext.request.contextPath}/freeboard_list.do?pageNo=${pb.endPageOfPageGroup+1}">
    ▶<!-- <img src="img/right_arrow_btn.gif"> --></a>
    </c:if>
    </p>
cs


위와 같이 JSP에서 pagingBean 객체를 이용해서 페이지 처리를 하였습니다.















































댓글