Keep going

페이징 화면 처리 본문

Records/Spring Framework

페이징 화면 처리

코딩천재홍 2021. 3. 11. 05:02

 

URL 파라미터로 정상적으로 원하는 페이지로 이동하는 것이 확인되었다면, 화면 밑에 페이지 번호를 표시해 사용자가 페이지 번호를 클릭할 수 있게 처리한다.

 

- 페이지를 보여주는 작업- 

  • 브라우저 주소창에서 페이지 번호를 전달해서 결과를 확인하는 단계
  • JSP에서 페이지 번호를 출력하는 단계
  • 각 페이지 번호에 클릭 이벤트 처리
  • 전체 데이터 개수를 반영해서 페이지 번호 조절

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

  • 현재 페이지 번호(page)
  • 이전과 다음으로 이동 가능한 링크의 표시 여부(prev, next)
  • 화면에서 보여지는 페이지의 시작 번호와 끝 번호(startPage, endPage)

 

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

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

사용자가 19 페이지를 본다면 11부터 시작해야 하기 때문.

끝 번호를 먼저 계산해 두는 것이 수월하다.

 

끝 번호 계산
this.endpage = (int)(Math.ceil(페이지번호 / 10.0)) * 10;
cs

Math.ceil : 소수점을 올림 처리

 

끝번호를 먼저 계산하는 이유는 시작 번호를 계산하기 수월하기 때문.

만일 화면에 10개 씩 보여준다면 시작 번호는 무조건 끝 번호에서 9라는 뺀 값이 된다.

 

시작 번호 계산
this.startPage = this.endPage - 9;
cs

 

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

만일 10개씩 보여주는 경우 전체 데이터 수가 80이라고 가정하면 끝 번호는 10이 아닌 8이 되어야 한다.

만일 끝 번호와 한 페이지당 출력되는 데이터 수의 곱이 전체 데이터 수 보다 크다면 끝 번호는 다시 total을 이용해서 계산되면 된다.

 

total을 이용한 endPage의 재계산
realEnd = (int)(Math.ceil((total*1.0) / amount));
 
if(realEnd < this.endPage) {
 
   this.endPage = realEnd;
 
}
cs

 

이전과 다음

이전의 경우는 시작 번호가 1보다 큰 경우라면 존재하게 된다.

this.prev = this.startPage > 1;
cs

다음으로 가는 링크의 경우 위의 realEnd가 끝 번호보다 큰 경우에만 존재하게 된다.

this.next = this.page < realEnd;
cs

 


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

페이징 처리를 위해 여러 정보가 필요하다면 클래스를 구성해서 처리 하는 방식이 편하다.

Controller 계층에서 JSP 화면에 전달 할때도 객체를 생성해 Model에 담아 보내는 과정이 단순해진다.

 

pageDTO 클래스
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와 전체 데이터 수를 파라미터로 지정한다.

BoardControoler에서는 PageDTO를 사용할 수 있도록 Model에 담아서 화면에 전달해 줄 필요가 있다.

 

BoardController 클래스의 list()
@GetMapping("/list")
    public void list(Criteria cri, Model model) {
        
        log.info("list: " + cri);
        model.addAttribute("list", service.getList(cri));
        model.addAttribute("pageMarker"new PageDTO(cri, 123));
        
    }
cs

 

 


14.3 JSP에서 페이지 번호 출력

JSP에서 페이지 번호를 출력하는 부분은 JSTL을 이용해서 처리

(SB Admin2는 부트 스트랩 기반으로 https://v4-alpha.getbootstrap.com/components/pagination과 과 같이 관련 링크들에 필요한 예제들이 있다)

 

list.jsp 일부
<div class='pull-right'>
    <ul class="pagination">
        <c:if test="${pageMaker.prev }">
            <li class="paginate_button previous"><a href="#">Previous</a>
            </li>
        </c:if>
 
        <c:forEach var="num" begin="${pageMaker.startPage }"
        end="${pageMaker.endPage }">
        <li class="paginate_button next"><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>
<!--  end Pagination  -->
cs

Modal 창의 아래쪽에 별도의 <div class = 'row'>를 구성하고 페이지 번호들을 출력한다.

pageMarker 라는 이름으로 전달된 PageDTO를 이용해서 화면에 페이지 번호들을 출력한다.

 

 

14.3.1 페이지 번호 이벤트 처리

JavaScript를 통해서 처리하는 방식 이용.

우선 페이지와 관련된 <a> 태그의 href 속성값으로 페이지 번호를 가지도록 수정한다.

 

list.jsp의 일부
<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>
                <!--  end Pagination  -->
cs

이제 화면에서는 <a> 태그는 href 속성값으로 단순히 번호만을 가지게 변경된다.

이제 <a> 태그가 원래의 동작을 못하도록 JavaScript 처리를 한다.

실제 페이지를 클릭하면 동작을 하는 부분은 별도의 <form> 태그를 이용해서 처리하도록 한다.

 

list.jsp의 일부
<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
list.jsp의 일부
<script type="text/javascript">
                $(document).ready(function() {
                    var result='<c:out value="${result}"/>';
                    checkModal(result);
                    
                    history.replaceState({}, nullnull);
                    
                    function checkModal(result) {
                        if(result==='' || history.state) {
                            return;
                        }
                        
                        if(parseInt(result) > 0) {
                            $(".modal-body").html("게시글 " + parseInt(result) + "번이 등록되었습니다.");
                        }
                        
                        $("#myModal").modal("show");
                    }
                    
                    $("#regBtn").on("click"function() {
                        self.location="/board/register";
                    });
                    
                    var actionForm = $("#actionForm");
                    
                    $(".paginate_button a").on("click"function(e) {
                        e.preventDefault();
                        console.log('click');
                        actionForm.find("input[name='pageNum']").val($(this).attr("href"));
                        actionForm.submit();
                    });
                });
            </script>
cs

<a> 태그를 클릭해도 페이지 이동이 없도록 preventDefault( )처리를 하고,

<form> 태그 내 pageNum 값은 href 속성값으로 변경한다.

 


14.4 조회 페이지로 이동

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

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

form 태그에 추가로 게시물의 번호를 같이 전송하고, action 값을 조정해서 처리할 수 있다.

페이지 번호는 조회 페이지에 전달되지 않기 때문에 조회 페이지에서 목록 페이지로 이동할 때는 아무런 정보가 없이 다시 '/board/list'를 호출하게 되는것이다.

 

<script type="text/javascript">
                $(document).ready(function() {
                    var result='<c:out value="${result}"/>';
                    checkModal(result);
                    
                    history.replaceState({}, nullnull);
                    
                    function checkModal(result) {
                        if(result==='' || history.state) {
                            return;
                        }
                        
                        if(parseInt(result) > 0) {
                            $(".modal-body").html("게시글 " + parseInt(result) + "번이 등록되었습니다.");
                        }
                        
                        $("#myModal").modal("show");
                    }
                    
                    $("#regBtn").on("click"function() {
                        self.location="/board/register";
                    });
                    
                    var actionForm = $("#actionForm");
                    
                    $(".paginate_button a").on("click"function(e) {
                        e.preventDefault();
                        console.log('click');
                        actionForm.find("input[name='pageNum']").val($(this).attr("href"));
                        actionForm.submit();
                    });
                    $(".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();
                    });
                });
            </script>
cs

 

<form> 태그에 추가로 bno 값을 전송하기 위해서 <input> 태그를 만들어 추가하고,

<form> 태그의 action은 '/board/get'으로 변경한다.

게시물의 제목을 클릭했을 때 pageNum과 amount 파라미터가 추가로 전달되는 것을 볼 수 있다.

 

 

 

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

조회 페이지에 다시 목록 페이지로 이동하기 위한 파라미터들이 같이 전송되었다면 조회 페이지에서 목록으로 이동하기 위한 이벤트를 처리해야 한다.

BoardController의 get( ) 메서드는 원래는 게시물의 번호만 받도록 처리되었지만, 추가적인 파라미터가 붙으면서 Criteria를 파라미터로 추가해서 받고 전달한다.

 

BoardController 클래스
@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 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' 버튼을 통해서 수정/삭제 페이지로 이동하게 된다.

BoardController get( ) 메서드에서 /get 과 /modify를 같이 처리하므로 별도의 추가적인 처리 없이도 Criteria를 Model에 cri라는 이름으로 담아서 전달한다.

 


14.5 수정과 삭제 처리

modify.jsp에서 form 태그를 이용해 데이터를 처리한다.

거의 입력과 비슷한 방식으로 구현되는데 이제 pageNum과 amount라는 값이 존재하므로 <form> 태그내에서 같이 전송할 수 있게 수정해야 한다.

 

modify.jsp 일부
<div class="panel-heading">Board Modify page</div>
            <!-- /.panel-heading -->
            <div class="panel-body">
                <form role="form" action="/board/modify" method="post">
                <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 방식으로 진행하는 수정과 삭제 처리는 BoardController에서 각각의 메서드 형태로 구현되었으므로 페이지 관련 파라미터 처리를 위해 변형해야 한다.

    @PostMapping("/modify")
    public String modify(BoardVO board, @ModelAttribute("cri") Criteria cri, RedirectAttributes rttr) {
        log.info("modify: " + board);
        if(service.modify(board)) {
            rttr.addFlashAttribute("result""sucess");
        }
        rttr.addAttribute("pageNum", cri.getPageNum());
        rttr.addAttribute("amount", cri.getAmount());
        return "redirect:/board/list";
    }
    
    @PostMapping("/remove")
    public String remove(@RequestParam("bno") Long bno, @ModelAttribute("cri") Criteria cri, RedirectAttributes rttr) {
        log.info("remove..." + bno);;
        if(service.remove(bno))
        {
            rttr.addFlashAttribute("result""success");
        }
        rttr.addAttribute("pageNum", cri.getPageNum());
        rttr.addAttribute("amount", cri.getAmount());
        return "redirect:/board/list";
    }
cs

메서드의 파라미터에는 Criteria가 추가된 형태로 변경되고, RedirectAttributes 역시 URL 뒤에 원래의 페이지로 이동하기 위해서 pageNum과 amount 값을 가지고 이동하게 수정한다.

 

 

14.5.2 수정/삭제 페이지에서 목록 페이지로 이동

페이지 이동의 마지막은 수정/삭제를 취소하고 다시 목록 페이지로 이동하는 것이다.

목록 페이지는 오직 pageNum과 amount만을 사용하므로 <form> 태그의 다른 내용들은 삭제하고 필요한 내용만을 다시 추가하는 형태가 편리하다.

 

modify.jsp
<script type="text/javascript">
$(document).ready(function() {
    var formObj = $("form");
    $('button').on("click"function(e) {
        e.preventDefault();
        var operation = $(this).data("oper");
    
        console.log(operation);
    
        if(operation === 'remove') {
            formObj.attr("action""/board/remove");
        } else if(operation==='list') {
            //move to list
            formObj.attr("action""/board/list").attr("method","get");
            var pageNumTag = $("input[name='pageNum']").clone();
            var amountTag = $("input[name='amount']").clone();
            formObj.empty();
            formObj.append(pageNumTag);
            formObj.append(amountTag);
        }
        formObj.submit();
    });
    });
</script>
cs

 

만일 사용자가 'List' 버튼을 클릭한다면 <form> 태그에서 필요한 부분만 잠시 복사해서 보관해 두고,

<form> 태그 내의 모든 내용은 지워버린다.

이후에 다시 필요한 태그들만 추가해서 '/board/list'를 호출하는 형태를 이용한다.

 

 


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

최종적으로는 데이터베이스에 있는 실제 모든 게시물의 수를 구해 PageDTO를 구성할 때 전달해 줘야 한다.

 

BoardMapper 인터페이스에 getTotalCount( ) 메서드를 정의하고 XML을 이용해서 SQL을 처리한다.

BoardMapper 인터페이스
package org.zerock.mapper;
 
import java.util.List;
 
//import org.apache.ibatis.annotations.Select;
import org.zerock.domain.BoardVO;
import org.zerock.domain.Criteria;
 
public interface BoardMapper {
 
//    @Select("select * from tb1_board where bno >0")
    public List<BoardVO> getList();
    
    public List<BoardVO> getListWithPaging(Criteria cri);
    
    public void insert(BoardVO board);
    
    public void insertSelectKey(BoardVO board);
    
    public BoardVO read (Long bno);
    
    public int delete (Long bno);
    
    public int update(BoardVO board);
    
    public int getTotalCount(Criteria cri);
 
}
 
cs
BoardMapper.xml
<select id="getTotalCount" resultType="int">
        select count(*) from tbl_board where bno > 0
    </select>
cs

 

BoardService 인터페이스 일부
package org.zerock.service;
 
import java.util.List;
 
import org.zerock.domain.BoardVO;
import org.zerock.domain.Criteria;
 
public interface BoardService {
 
    public void register(BoardVO board);
    
    public BoardVO get(Long bno);
    
    public boolean modify(BoardVO board);
    
    public boolean remove (Long bno);
    
//    public List<BoardVO> getList();
    public List<BoardVO> getList(Criteria cri);
 
    public int getTotal(Criteria cri);
}
 
 
 
cs
BoardServiceImpl 클래스
@Override
    public int getTotal(Criteria cri) {
        log.info("get total count");
        return mapper.getTotalCount(cri);
    }
cs

 

BoardService의 getTotal( )에 굳이 Criteria는 파라미터로 전달될 필요가 없기는 하지만, 목록과 전체 데인터 개수는 항상 같이 동작하는 경우가 많기 때문에 파라미터로 지정한다.

 

BoardController 클래스 일부
@GetMapping("/list")
    public void list(Criteria cri, Model model) {
        log.info("list: " + cri);
        model.addAttribute("list", service.getList(cri));
        //model.addAttribute("pageMaker", new PageDTO(cri, 123));
        int total = service.getTotal(cri);
        log.info("total: " + total);
        model.addAttribute("pageMaker"new PageDTO(cri, total));
    }
cs

 


출처 : 코드로 배우는 스프링 웹 프로젝트 [구멍가게 코딩단]

'Records > Spring Framework' 카테고리의 다른 글

REST 방식으로 전환  (0) 2021.03.16
검색처리  (0) 2021.03.15
MyBatis와 스프링에서 페이징 처리  (0) 2021.03.11
오라클 데이터베이스 페이징 처리  (0) 2021.03.05
화면 처리  (0) 2021.02.21
Comments