프로그래밍/전자정부프레임워크(eGov)

스프링 게시판 페이징 구현하기 - (1)

Jay Tech 2017. 3. 12. 12:16
반응형


이렇게 페이징을 구현하려면 먼저 페이지에 관련된 VO 객체가 필요하다.


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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
public class CmmnVO {
    
    private static final long serialVersionUID = 1L;
    
    private long rows = 2;    
    
    private long page = 1;    
    
    private long totalPage;
    
    private long startPage = 1;
    
    private long endPage;
    
    private long pageScale = 3;
 
    public long getRows() {
        return rows;
    }
 
    public void setRows(long rows) {
        this.rows = rows;
    }
 
    public long getPage() {
        return page;
    }
 
    public void setPage(long page) {
        this.page = page;
    }
 
    public long getTotalPage() {
        return totalPage;
    }
 
    public void setTotalPage(long totalPage) {
        this.totalPage = totalPage;
    }
 
    public long getStartPage() {
        return startPage;
    }
 
    public void setStartPage(long startPage) {
        this.startPage = startPage;
    }
 
    public long getEndPage() {
        return endPage;
    }
 
    public void setEndPage(long endPage) {
        this.endPage = endPage;
    }
 
    public long getPageScale() {
        return pageScale;
    }
 
    public void setPageScale(long pageScale) {
        this.pageScale = pageScale;
    }
 
    public static long getSerialversionuid() {
        return serialVersionUID;
    }    
}
cs


rows : 현재 페이지에 보여질 row수


page : 현재 보여질 페이지 수


totalPage : 총 페이지 수


startPage : 시작페이지수


endPage : 끝 페이지 수


pageScale : 한 페이지에 보여질 페이지 수


이렇게 6개의 필드가 들어가게 된다. 그리고 이 CmmnVO를 상속받는 특정페이지에 해당하는 객체, 그러니까 각 페이지 마다 페이징이 다를 수 있으므로 위와 같이 공통으로 하나 빼 놓고 알아서 이것을 상속받는 특정 PagingVO를 하나 더 만든다. 여기에서는 다른 기능이 없으므로 그냥 extends만 해놓고 안에 추가되는 내용은 없다.


위치는


 service 밑에 VO를 위치시면 된다. (impl바로밑에 뜨면된다 impl안에 아님)


위치에 대한 자세한 내용은 다른 게시물을 참조

http://pjh3749.tistory.com/128



SQL 마이바티스 쪽의 쿼리를 먼저 만들어 보자. 쿼리는 2개가 필요하다. 진짜 값들을 가져오는 쿼리갯수파악을 위한 쿼리 이렇게 2개가 있어야 한다.


1
2
3
4
5
6
7
8
9
10
11
SELECT a.*,
         ROWNUM rnum,
         FLOOR((ROWNUM - 1/#{rows} + 1) pageNumber
                  FROM  (
               SELECT ID,
               USER_NAME,
               AGE,
               COUNTRY,
               ETC
          FROM [table]
          ORDER BY ID ASC) a;
cs


이런식으로 각 페이지가 어떤 페이지 넘버를 갖는 지 뽑는다. ${rows}는 전달할 각 페이지의 줄 숫자 이므로 pagingVO로 넘겨서 받게 된다. 예를들어 2번째 줄은 페이지 넘버가 1이다. (2-1)/3 +1 =1 이므로. FLOOR는 버림을 담당하는 함수이다. 즉 첫 줄은 1 둘째 줄은 1 셋째 줄은 2 이런식으로 하게 된다. 한 페이지에 나오는 줄 수는 자기 임의 대로 하면 되므로 상관없다. 여기서는 2줄만 나오게 하였다.


그리고 이 전체를 감싸는 쿼리를 한 번 더 작성한다. 왜냐하면 어떤 페이지가 눌리는지에 따라서 가져오는 값들이 달라지기  때문이다. 즉 내가 2페이지를 눌렀으면 2페이지에 해당하는 줄 2줄을 갖고와야 하는것과 같다.


1
2
3
4
5
SELECT  a.*
              FROM  (
 
[위의쿼리]
) a
             WHERE  a.pageNumber = #{page}
cs

이런식으로 #{page}를 가져왔다. 이것은 어떤 페이지가 눌렸는지에 따라서 거르는 것이다. 

다음 쿼리를 보자.

1
2
3
4
5
SELECT  count(*) TOTAL_TOT_CNT
              , CEIL(count(*/ #{rows}) TOTAL_PAGE
      FROM  welcome_web
      WHERE  1=1
        ORDER BY  ID ASC
cs

일단 전체 카운트가 필요하므로 전체 갯수를 구한다. 그것을 TOTATL_TOT_CNT라고 한다.
그리고 전체 페이지수를 구한다. CEIL은 올림함수이다. #{rows}는 아까 말했듯이 자기가 임의대로 몇줄씩 보여줄지 결정하는 것이다.


이로써 쿼리를 완성했다. 쿼리이름을 임의로 짓는다. 첫 번째 쿼리 id를 selectPagingList라고 하고 두 번째 쿼리 id를 selectPagingListCnt라고 하겠다. 


이제 컨트롤러로 가보자.


1
2
3
4
5
6
7
8
@RequestMapping(value = "paging.do")
    public String paging(pagingVO pagingVo, ModelMap model) throws Exception {
        
        EgovMap pagingListCnt = pagingService.selectPagingListCnt(pagingVo);
        System.out.println("totalPage :" + pagingListCnt.get("totalPage"));
        
        HashMap<String, Object> resMap = new HashMap<String, Object>();
        resMap.put("total"pagingListCnt.get("totalPage"));
cs



아까 selectPagingListCnt에서 전체카운트와 페이지카운트를 구하는 것을 보았다. 이것의 returnType 은 egovMap이어서 EgovMap으로 받았다. 전체 페이지를 구해서 HashMap에 넣었다. HashMap에 넣는 이유는 이 paging함수 끝자락에서 뷰로 넘길때 같이 넘겨주기 위함이다. 그것의 key를 "total"이라고 하였다. 첫 번째 파라미터로 페이징vo를 받았다. 이것은 자동맵핑인데 이것에 대한 자세한 내용은 이게시물을 참조바란다.

지금부터 로직을 구하는 공식을 적용한다.


* 페이지 그룹


페이지 그룹은 특정 페이지가 어떤 그룹에 속했는지에 대한 속성이다. 예를 들어 나는 밑의 페이지번호를 3개씩 보여주고 싶다. 1,2,3 다음버튼누르면 4,5,6 이렇게 나오도록 말이다. 그러면 2페이지는 1그룹이다. 왜냐하면 1그룹은 1,2,3 이 같이있고 2 그룹은 4,5,6이 같이 있기 때문이다. 다시 그럼 5페이지는 몇 그룹일까? 2그룹이다. 


int pageGroup = (int)Math.ceil((double)pagingVo.getPage()/pagingVo.getPageScale());


이 그룹 또한 resMap에 "pageGroup"으로 넘긴다.


* 시작 페이지


처음 로드될 때 시작되는 페이지이다. 즉, 처음 게시판에 들어갔을 때는 당연히 1번부터 시작이겠고 >> 이 버튼을 누르면 3개씩 보여졌을 경우(현재 가정상태였음) 4번부터 시작이된다는 것을 나타내는 속성이다.


long startPage = (pageGroup -1) * pagingVo.getPageScale() + 1;


이 값 또한 resMap에 넣는다. 앞으로 나오는 모든 것들도 다 해시맵에 넣자.


* 끝 페이지


시작 페이지와 같은 원리로 해당 그룹에서의 끝 페이지를 구하는 것이다.


long endPage = startPage + pagingVo.getPageScale() - 1;



* 이전 페이지


이전 페이지의 핵심은 그룹에서의 이전 페이지를 누를 수 있는지에 대한 것이다. 즉 처음 1,2,3 이라고 나왔을 때는 당연히 이전 페이지가 없다. 4,5,6 부터 이전 페이지의 버튼이 존재하게 되는 것이다. 그러므로 jsp에서 if로 걸러주어서 이전버튼이 나올지말지를 결정하는 식으로 진행한다. 구분을 위해서 첫 번째 그룹은 이 값이 음수가 나오게 하였다.


long prePage = (pageGroup -2) * pagingVo.getPageScale() + 1;


* 다음 페이지


이전 페이지와 같은 원리로 다음페이지 버튼 >> 을 다는 속성이다.


long nextPage = pageGroup * pagingVo.getPageScale() + 1;



이제 뷰로 내리면 된다.


1
2
3
4
5
6
7
List<Map> pagingList = pagingService.selectPagingList(pagingVo);
        
model.addAttribute("pagingList", pagingList);        
model.addAttribute("resMap", resMap);        
     
return "paging/paging.tiles";
cs


line 1에 내용은 pagingVo를 넘겨서 어떤 줄을 가져올지 결정하는 것이다. 맨위에 쿼리 설명에서 했다.


이제 컨트롤러 쪽은 완성했다. jsp 뷰쪽을 수정하면 되겠다.


표 바로 밑에 페이지 번호가 나오는 코드를 작성하면 된다. 부트스트랩 표를 이용할 것인데 부트스트랩 표에 관한 내용은 여기 설명되어 있으니 참고바란다.

http://pjh3749.tistory.com/140


표 바로 밑에

<div class="content">

   <ul class="pagination">


일단 얘네들로 감싼다.


 보다시피 첫 페이지는 이전 버튼이 없다.



1
2
3
4
5
<c:if test="${resMap.pageGroup > 1}">
   <li>
      <a href = "javascript:fnGoPaging(<c:out value='${resMap.prePage}'/>)"> » </a>
   </li>
 </c:if>
cs


아까 페이지 그룹을 설명할 때 첫 그룹은 1부터 시작이다. 1,2,3 얘네들은 이전버튼이 없으므로 line 1 처럼 if문으로 걸러준다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
<c:forEach var="i" begin="${resMap.startPage}" 
end="${resMap.endPage > resMap.total ? resMap.total : resMap.endPage}" 
varStatus="status">
 <c:choose>
    <c:when test="${resMap.page eq i}">
       <li class="active">                         
          <a href="javascript:fnGoPaging(${i});">${i}</a>
       </li>
    </c:when>
    <c:otherwise>
       <li>                         
          <a href="javascript:fnGoPaging(${i});">${i}</a>
       </li>
    </c:otherwise>                         
 </c:choose>
</c:forEach>

cs


로직을 계산해보면 각 페이지가 눌리는 것에 따라 1,2,3 이 나오고 4가 나오게 한다. 12,3 어떤 것을 누르던 total은 4이다(총 페이지 수) 그러면 반복문을 3번 돌게된다. i로 3개를 돌려서 찍는다. 그리고 line 4는 진짜 눌린 페이지와 i가 일치하게 되면 active를 주는데 이것은 그냥 번호가 눌린 곳에 색을 칠해주는 것이다. 4페이지는 end조건에서 resMap.endPage로 걸리게 된다. ? 랑 : 은 3항연산자인데 찾아보면 쉽게 알 수 있다.


마지막으로 다음버튼이다. 위의 것을 보고 나서 보면 쉽게 이해될 것이다. 


1
2
3
4
5
<c:if test="${resMap.nextPage <= resMap.total}">
    <li>
       <a href="javascript:fnGoPaging(<c:out value='${resMap.nextPage}'/>)">»</a>
    </li>
</c:if>
cs


fnGoPaging은 눌렸을 때 이동하는 부분이다. 즉, 스크립트 쪽에서 저 함수를 단순히 구현만 해주면 될 것이다.


function fnGoPaging(page) {

  location.href = "http://localhost:8080/sample/paging.do?page="+page

}


?page로 파라미터 page를 넘겼다. 아까 컨트롤러 쪽에 첫 번째 파라미터로 pagingVO를 넘긴것을 기억해보자. 그 객체에 필드 중 page라는 것이 있을 것이다. 그것에 자동으로 맵핑이 되어 들어가는 것이다.

값 던지고 받거나 페이징 로직이아닌 전체적인 흐름이 이해가 가지 않는다면 이 블로그의 전자정부프레임워크와 스프링에 대한 글들을 찬찬히 살펴보길 바란다.


질문있으시면 댓글 남겨주세요~







반응형