읽은 책 정리/코드로 배우는 스프링 웹 프로젝트

[Spring] 14 페이징 화면처리

포포015 2020. 12. 21. 23:46

 

1
2
3
4
5
     <form id='operForm' action="/board/modify" method="get">
                        <input type='hidden' id='bno' name='bno' value='<c:out value="${board.bno }"/>'>
                         <input type='hidden' name='pageNum' value='<c:out value="${cri.pageNum }"/>'>
                          <input type='hidden' name='amount' value='<c:out value="${cri.amount }"/>'>
                        </form>
cs

페이지를 보여주는 작업은 다음과 같은 작업을 통해서 진행한다.

- 브라우저 주소창에서 페이지 번호를 전달해서 결과를 확인하는 단계

- JSP에서 페이지 번호를 출력하는 단계

- 각 페이지 번호에 클릭 이벤트 처리

- 전체 데이터 개수를 반영해서 페이지 번호 조절

 

페이지 처리는 단순히 링크의 연결이기 때문에 어렵지 않지만, 목록페이지에서 조회페이지,

수정 삭제 페이지까지 페이지 번호가 계속 유지되어야 하기때문에 신경 써야할게 많다.

 

14.1 페이징 처리 할때 필요한 정보들

- 현재 페이지 번호(page)

- 이전과 다음으로 이동 가능한 링크의 표시 여부(prev,next)

- 화면에서 보여지는 페이지의 시작 번호와 끝 번호(startPage, endPage)

 

14.1.1 끝 페이지 번호와 시작 페이지 번호

페이징 처리를 하기위해서는 우선적으로 필요한 정보는 현재 사용자가 보고 있는 페이지(page)의 정보

예를 들어, 사용자가 5페이지를 본다면 화면의 페이지 번호는 1부터 시작하지만 사용자가 19페이지를 본다면,

11부터 시작해야 하기때문(10 개씩 출력한다는 기준)

 

끝번호를 먼저 계산 해두는것이 수월함. 끝번호는 다음과 같은공식으로 구할수 있다.

1
this.endPage = (int)(Math.ceil(페이지번호 / 10.0)) * 10;
cs

 - 1페이지의 경우 : Math.ceil(0.1) * 10 = 10

 - 10페이지의 경우: Math.ceil(1) * 10 = 10

 - 11페이지의 경우:Math.ceil(1.1) * 10 = 20

끝번호 페이지는 개선해야할부분이 있다. 전체 데이터 수가 적다면 10페이지로 끝나면안되는 상황이 있기때문.

끝번호를 미리 구해두면 시작번호(startPage)를 계산하기 수월함.

 

화면에서 10개씩 보여준다면 시작번호(startPage)는 무조건 끝번호(endPage)에서 9라는 값을 뺸 값이됨

1
this.startPage = this.engPage - 9;
cs

 

끝 번호(endPage)는 전체 데이터수(total)에 의해 영향을받는다.

예를들어 10개씩 보여주는경우 전체데이터수(total)수가 80개라하면 끝번호는 10이 아닌8이 되어야함.

1.먼저 전체데이터수(total)을 이용해 진짜 끝페이지(realEnd)가 몇번까지 되는지 계산.

2. 만일 진짜 끝페이지(realEnd)가 구해둔 끝번호(endPage)보다 작다면 끝 번호(endPage)는 작은값이 되어야함

1
2
3
4
realEnd = (int) (Math.ceil((total * 1.0) / amount) );
if(realEnd < this.endPage) {
    this.endPage = realEnd;
}
cs

 

이전과(prev)과 다음(next)

이전(prev)의 경우 시작번호(startPage)가 1보다 큰경우라면 존재.

1
this.prev = this.startPage > 1;
cs

다음(next)으로 가는 링크의 경우 위의 realEnd가 끝번호(endPage)보다 큰경우에만 존재

1
this.next = this.endPage < realEnd;
cs

 

14.2 페이징처리를 위한 클래스 설계

클래스를 구성하면 Controller 계층에서 JSP 화면에 전달할때에도 객체를 생성해서

Model에 담아 보내는 과정이 단순해지는 장점이 있다.(재사용)

PageDTO 클래스 설계

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
package org.zerock.domain;
 
import lombok.Getter;
import lombok.ToString;
 
@Getter
@ToString
public class PageDTO {
 
    private int startPage;
    private int endPage;
    private boolean prev,next;
    
    private int total;        //전체 데이터 
    private Criteria cri;     
    
    public PageDTO(Criteria cri, int total) {
        this.cri = cri;
        this.total = total;
        
        this.endPage = (int) (Math.ceil(cri.getPageNum() / 10.0)) * 10;
        this.startPage = this.endPage - 9;
        
        int realEnd = (int) (Math.ceil(total * 1.0/ cri.getAmount());
        
        if(realEnd < this.endPage) {
            this.endPage = realEnd;
        }
        
        this.prev = this.startPage > 1;
        this.next = this.endPage < realEnd;
    }
    
    
}
 
cs

PageDTO는 생성자를 정의하고, Criteria와 전체 데이터수(total)를 파라미터로 지정

Criteria 안에는 페이지를 보여주는 데이터수(amount)와 현재 페이지 번호(pageNum)을 갖고 있다(1페이지 10개씩)

BoardController안에서 PageDTO를 사용할수 있도록, Model에 담아서 화면에 전달해줘야함.

1
2
3
4
5
6
@GetMapping("/list")
    public void list(Criteria cri, Model model) {
        log.info("컨트롤러 list");
        model.addAttribute("list", service.getList(cri));
        model.addAttribute("pageMaker"new PageDTO(cri, 123));
    }
cs

list 메소드는 'pageMaker'라는 이름으로 PageDTO 클래스에서 객체를 만들어서 Model에 담아준다.

전체데이터(total)을 구성하기 위해서는 임시 데이터로 123을 지정.

 

14.3 JSP에서 페이지 번호 출력

list.jsp 하단에 페이지 번호 출력 (부트스트랩적용)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
                            <h2>${pageMaker}</h2> //pageMaker의 toString 값을 확인하기 위한코드
                        <div class='pull-rigth'>
                            <ul class="pagination">
                                <c:if test="${pageMaker.prev}">
                                    <li class="paginate_button previous"><a href="#">Prev</a></li>
                                </c:if>
                            
                                <c:forEach var="num" begin="${pageMaker.startPage}" end="${pageMaker.endPage}">                                
                                    <li class="paginate_button"><a href="#">${num}</a></li>
                                </c:forEach>
                            
                                <c:if test="${pageMaker.next }">
                                    <li class="paginate_button next"><a href="#">Next</a></li>
                                </c:if>
                            </ul>
                        </div>
cs

 

14.3.1 페이지 번호 이벤트 처리

<a> 태그의 href 속성으로 이용하는 방법도 있지만 ,직접 링크를 처리하는 방식의 경우

검색 조건이 붙고 난후에 처리가 복잡하게 되므로 자바스크립트를 이용.

 

우선 페이지와 관련된 <a>태그의 href 속성값으로 페이지 번호를 갖게 수행

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    <div class='pull-right'>
                    <ul class="pagination">
 
                        <c:if test="${pageMaker.prev}">
                            <li class="paginate_button previous"><a
                                href="${pageMaker.startPage -1}">Previous</a></li>
                        </c:if>
 
                        <c:forEach var="num" begin="${pageMaker.startPage}"
                            end="${pageMaker.endPage}">
                            <li class="paginate_button  ${pageMaker.cri.pageNum == num ? "active":""} ">
                                <a href="${num}">${num}</a>
                            </li>
                        </c:forEach>
 
                        <c:if test="${pageMaker.next}">
                            <li class="paginate_button next"><a
                                href="${pageMaker.endPage +1 }">Next</a></li>
                        </c:if>
 
 
                    </ul>
                </div>
cs

이제부터 화면에서는 <a>태그는 href 속성값으로 단순히 번호만 가지게 됨.

이 상태에서 페이지 번호를 클릭하면 URL이 존재하지 않기때문에 문제가 생기게됨.

 

실제 페이지를 클릭하면 동작하는 부분은 별도의 <form>태그를 이용해서 처리.(list.jsp)

1
2
3
4
      <form id='actionForm' action="/board/list" method='get'>
          <input type='hidden' name='pageNum' value= '${pageMaker.cri.pageNum}'>
          <input type='hidden' name='amount' value= '${pageMaker.cri.amount}'>  
      </form>
cs

 

기존에 동작하던 자바스크립트 부분은 아래와 같이 코드를 추가

<a>태그가 원래의 동작을 못하도록 자바스크립트 처리를 해줌.

list.jsp 에서 <form>태그를 추가해서 URL의 이동을 처리하도록 변경

1
2
3
4
5
6
7
8
9
10
var actionForm = $("#actionForm");
        
        $(".paginate_button a").on("click", function(e) {
              
            e.preventDefault(); //기본 동작 제한    
 
            actionForm.find("input[name='pageNum']").val($(this).attr("href"));
            actionForm.submit();
                    
            });
cs

 

14.4 조회 페이지 이동

목록 화면에서 페이지 번호를 클릭하면 정상적으로 원하는 페이지로 이동하는데,

사용자가 3페이지에 있는 게시글을 클릭한후 다시 목록으로 이동하면 무조건 1페이지 목록으로 이동한다.

이를 해결하기 위해서는 조회페이지로 갈때 현재의 목록페이지 pageNum과 amount를 같이 전달해야한다.

이런경우 페이지이동에 사용했던 <form>태그에 추가로 게시물의번호를 같이 전송하고 action값을 조정한다.

 

원래 게시물의 제목에선 조회페이지로 이동시 <a href>태그로 이동할수 있는 링크를 처리했었다.

페이지 번호는 조회페이지에 전달되지 않기때문에 조회페이지에서 목록페이지로 이동할때는 정보가 없어져서 

다시 /board/list만 호출하게 된다.

직접 링크로 연결된 경로를 페이지 이동과 마찬가지로<form>태그를 이용해 처리

<a> 태그에는 이동하려는 게시물의 번호만 가지게 수정 (이벤트 처리를 수월하게 하기위해 class 속성을부여)

1
2
                       <td><a class='move' href='<c:out value="${board.bno}"/>'> <c:out value="${board.title }" /></a></td>
 
cs

자바스크립트 추가

게시물의 제목<a>태그를 클릭하면 <form>태그에 추가로 bno 값을 전송하기 위해 

<input>태그를 만들어 추가하고 <form>태그의 action 은 '/board/get'으로 변경하면 

게시물의 제목을 클릭할시 pageNum과 amount 파라미터가 추가로 전달됨.

1
2
3
4
5
6
7
8
9
10
 
        $(".move").on("click", function(e){
 
            e.preventDefault(); //기본 동작 제한
            actionForm.append("<input type='hidden' name='bno' value='"+$(this).attr("href")+"'>");
            actionForm.attr("action","/board/get");
            actionForm.submit();
            }
        );
 
cs

 

14.4.1 조회페이지에서 다시 목록페이지로 이동 - 페이지 번호 유지

조회 페이지에서 다시 목록페이지로 이동하기 위한 파라미터들이 같이 전송 되었다면, 

조회페이지에서 목록으로 이동하기 위한 이벤트 처리를 해야함.

Controller의 get()메서드는 원래 게시물의 번호(bno)만 받도록 처리 되어 있었지만,

추가적으로 Criteria를 파라미터로 추가해서 받고 전달한다.

1
2
3
4
5
6
7
    @GetMapping({"/get","/modify"})
    public void get(@RequestParam("bno") Long bno, @ModelAttribute("cri") Criteria cri, Model model) {
        
        log.info("/get or modify");
        
        model.addAttribute("board", service.get(bno));
    }
cs

@ModelAttribute는 자동으로 model에 데이터를 지정한이름으로 담아준다.

ModelAttribute를 사용하지 않아도 Controller에서 화면으로 파라미터가 전달되긴한다.

기존 get.jsp에서도 버튼을 클릭하면 <form>태그를 이용하는 방식 이었으므로,

필요한 데이터를 추가해서 이동함

컨트롤러에서 cri으로 전달된 Criteria객체를 이용해서 pageNum과 amount 값을 태그로 구성하고 이동하게 처리

1
2
3
4
5
     <form id='operForm' action="/board/modify" method="get">
                        <input type='hidden' id='bno' name='bno' value='<c:out value="${board.bno }"/>'>
                         <input type='hidden' name='pageNum' value='<c:out value="${cri.pageNum }"/>'>
                          <input type='hidden' name='amount' value='<c:out value="${cri.amount }"/>'>
                        </form>
cs

 

14.4.2 조회페이지에서 수정/삭제 페이지로 이동

조회 페이지에서 'Modify' 버튼을 통해서 수정/삭제 페이지로 이동하게된다.

수정/삭제 페이지에서는 다시 목록으로 가는 버튼이 존재해서, 동일하게 목록 페이지에서 필요한 파라미터를 처리.

하지만 Controller의 get()메서드 에서 '/get'과 , '/modify'를 같이 처리하므로 별도의 추가적인 처리없이도 파라미터 전달

 

14.5 수정과 삭제처리

modify.jsp도 <form>태그를 이용해 데이터를 처리한다.

여기선 전에 받아뒀던 파라미터 pageNum,amount값이 존재하므로

 

<form>태그 내에 같이 전송할수 있게 수정

1
2
    <input type='hidden' name='pageNum' value='<c:out value="${cri.pageNum}"/>'>
    <input type='hidden' name='amount' value='<c:out value="${cri.amount}"/>'>
cs

 

14.5.1 수정/삭제 처리후 이동

수정/삭제 처리후 POST 방식으로 진행중 Controller에 이미 각각 메서드형태로 구현되있으므로,

메서드를 변경해준다.(파라미터 cri 추가, 리다이렉트 데이터 추가 전달)

1
2
3
4
5
6
7
8
9
10
11
@PostMapping("/modify")
    public String modify(BoardVO board,@ModelAttribute("cri") Criteria cri, RedirectAttributes rttr) {
        log.info("modify:" + board);
        
        if(service.modify(board)) {
            rttr.addFlashAttribute("result""success");
        }
        rttr.addAttribute("pageNum",cri.getPageNum());
        rttr.addFlashAttribute("amount", cri.getAmount());
        return "redirect:/board/list";
    }
cs

 

14.6 MyBatis에서 전체 데이터의 개수 처리

모든작업에서 정상적으로 이루어진다면, 최종적으로 DB에 있는 실제 모든 게시물의 수(total)을

구해서 PageDTO를 구성할때 전달해준다. (컨트롤러 수정)

 mapperinterpace > mapper.xml> serivce > serviceImpl