안녕하세요. 지금까지는 게시글 목록 페이지에서 전체 게시글 1000개가 있으면 한 페이지 내에 1000개가 다 출력되고 있는데요.
이제 게시글 목록 페이지에서 [이전][1][2][3]....[10][다음] 과 같은 페이지 처리 부분을 넣어 3 을 클릭하면 3페이지 게시글 목록을
보여주고, 또 옆에 select 박스로 한 페이지당 출력할 게시글 수를 선택할 수 있게 만들어 사용자가 현재 페이지당 출력할 게시글
수도 선택할 수 있게끔 만들어 보도록 하겠습니다.
[ 1. 페이징 처리 구현 ]
페이징 처리를 할 때 가장 필요한게 무엇일까요? 저는 페이지를 처리하기 위한 VO 클래스가 필요하다 생각합니다. 그래서 가장 먼저 할 작업은
1) Criteria VO class와 PageMaker VO 클래스 두개를 만들겠습니다.
- Criteria 클래스 :
: - page : 현재 페이지를 나타내는 필드 변수
- perPageNum : 페이지당 표시할 게시글의 수
- getPageStart() : limit #{pageStart}, #{perPageNum} 부분에서 pageStart를 위한 부분
두 필드 변수를 담도록 할 것이고 이때 좀 중요한 부분이 있는데 Mybatis Mapper.xml 부분에서
게시글 목록을 검색할 때, 페이지에 맞는 게시글을 뽑아와야 합니다. 이때
select * from boardTable order by bno desc limit #{pagStart}, #{perPageNum}
이런식으로 검색을 해야합니다. 해석해보자면 boardTable에서 bno(게시글)을 내림차순으로 정렬해줍니다. 최신글이 가장
위로 올라와야하기 때문이죠. 그 다음 limit 부분을 보면 limit 시작 출력 행, 몇개씩 으로 ~부터 n개를 출력하겠다는 부분입니다.
이때, Mapper.xml에서 #{ } 이 안에 들어 있는 변수명은 getter를 호출해서 대입받는 것을 아실겁니다. 따라서
Criteria 클래스에선 getPageStart() 메서드가 추가로 반드시 필요합니다.!!!
이때, getPageStart()를 보면 return (page-1)*perPageNum을 볼 수 있을 것인데
1페이지일 때 -> 0 ~ 9 게시글
2페이지 -> 10 ~ 19
3페이지 -> 20 ~ 29
등을 적어놓고 생각해보면 왜 저런 식이 나왔는지 알 수 있습니다.
BoardService
BoardServiceImpl
다 마찬가지에요 페이징 처리 부분만 복사하세요.
@RequestMapping(value = "/listPage", method = RequestMethod.GET) public String listPage(@ModelAttribute("cri") Criteria cri, Model model) throws Exception { // 커맨드 객체로 Criteria를 매개변수로 넣어줘, 넘어오는 page와 perPageNum정보를 받습니다.
// 해당 cri 를 이용해서 service->dao->mapper.xml 순으로 접근하면서 DB처리를 해 cri 전달된
// 현재 페이지 정보를 기준으로 BoardVO 객체를 담은 ArrayList가 반환될 것입니다.
List<BoardVO> dto = service.listCriteria(cri);
// 이제 view jsp 페이지에서 페이징 처리를 위해 사용할 PageMaker 객체를 생성하고 PageMaker pageMaker = new PageMaker();
// 여기 부분을 "꼭" 해 주어야 한댔죠? Criteria를 set해주고 setTotalCount() 를 해주어야
// 페이징처리에 필요한 것들이 내부적으로 계산될 수 있도록 작성했다고 했습니다. pageMaker.setCri(cri); Integer totalNum = service.totalCount(); pageMaker.setTotalCount(totalNum);
// /views/board/listPage.jsp 에서 페이징 처리를 하기 위해 PageMaker 객체를 저장해 놓아야 할 것이고
// 당연히 화면에 게시글을 뿌려주기 위해서 꺼내온 dto도 저장을 해 주어야 할 것입니다.(model 객체에) model.addAttribute("pageMaker", pageMaker); model.addAttribute("list", dto); return "board/listPage"; }
5) 이제 /views/board/listPage.jsp 페이지에 대한 작업과 그에 필요한 사전 작업을 해봅시다.
listPage.jsp에서 이제 페이징 처리를 할 것인데, 페이지에 따라 a태그를 이용해서 href로 페이지를 띄우는 요청을 걸어놓겠죠?
그런데, 이러한 작업이 계속 반복될 것입니다. 그러면 이걸 하드코딩하는 것 보단 메서드로 만들어 놓으면 어떨가요?
ex) <a href="/board/listPage?page=3?perPageNum=10>[3]</a> 3쪽을 누르면 이런식으로 호출되도록 링크를 걸겁니다.
그러면 page변수가 넘어가서 Controller의 listPage(Criteria cri) 해 놓은 부분에서 cri의 setPage()와 setPerPageNum()을 호출해 데이터를
넣겠죠? 이러한 부분이 계속 반복된다는 것입니다. 근대 그나마 여기서는 칠만해요... 솔직히
근대 동적 검색을 지원하는 기능까지 붙이잖아요? 변수가 늘어나고? 엄청 짜증나기 시작할거에요... 그래서 미리 만들어 놓는 것이 좋습니다.
이러한걸 Spring은 지원하는대요 그게
"UriComponent"라는 겁니다.
<a href="/board/listPage?page=3?perPageNum=10>에서 ? 이 부분부터 "쿼리 문자열"이라고 하잖아요?
그 부분을 생성하는 메서드를 만들건대 page라는 정보와 perPageNum 정보가 필요하니까 두 변수를 가지고 있는 Criteria나 PageMaker에서
해주는게 좋겠죠? 여기서는 PageMaker 에서 해봅시다.
PageMaker class에서 makeQuery(int page) 라는 메서드를 만들어서 작성해봅시다.
7) listPage.jsp 페이지에 현재 페이지에 따른 게시글을 표시하도록 합시다.
6번에 작성한 코드 바로 윗 부분에 해주어야 [이전][1][2][3]...[10][다음] 윗 부분에 들어갈 게시글 목록부분입니다.
여기서 관심있게 볼 점은 게시글 목록의 경우 제목을 클릭하면, 해당 상세 읽기 페이지로 분기해야합니다.
따라서, href="/board/read${pageMaker.makeQuery(pageMaker.cri.page)}&bno=${boardVO.bno} 부분을 통해
page, perPageNum, bno(클릭했을 때 읽을 게시글 번호) 정보를 함께 넘겨 주어야 합니다.
그외에 boardVO 객체에 있는 게시글 등록 일 같은 정보를 출력하기 위해 jstl의 <fmt:formatDate 태그를 이용한 것을 알 수
있습니다.
그리고 마지막 부분에 글을 등록할 수 있는 글등록 버튼을 하나 넣었습니다.
8) listPage.jsp에서 글 등록 버튼을 클릭했을 때 글등록 페이지로 이동하도록 JQuery를 이용해보겠습니다.
<script>
// 글 작성처리에서 글 작성 성공시 redirect 직전에 RedirectAttribute 객체의 addFlashAttribute로 저장한 msg 변수
var result = '${msg}'; // 글작성 등록에 성공했을 때 msg 지정해 논 부분
if (result == 'SUCCESS') {
alert("처리가 완료되었습니다.");
}
// 이 부분이 8번에서 핵심 부분입니다.
$(document).ready(function() {
// 글 등록 버튼을 클릭하면...
$("#register").click(function() {
self.location = "/board/register"; // 글 작성 폼 페이지로 이동한다.
});
});
</script>
9) BoardController의 read() 부분을 수정해봅시다.
글 등록처리야 /board/register 를 호출하면서 글 등록 폼으로 잘 이동할 것이고 문제는 글 제목을 클릭했을 때
읽는 페이지 부분입니다. 우리가 /board/read?page=3&perPageNum=10 이런식으로 전달했기 때문에
이 read 부분에서도 page와 perPageNum을 Criteria를 이용해서 받아야만 합니다.
그리고 "진짜 진짜" 중요한 점은, 이 page정보와 perPageNum 정보를 숨김값(hidden) 으로 수정페이지나 삭제 페이지에 전달
해야만 합니다. 왜그럴까요?
왜냐하면 여러분이 3페이지의 게시글을 보고 있다가 글으로고 제목을 클릭했습니다. 그런데 글을 읽고 목록으로 다시 돌아오거나
삭제 페이지 혹은 수정페이지로 이동을 했다가 목록으로 돌아왔을 때 첫페이지로 돌아와있다면 어떨까요? 불편하겠죠?(그렇다해줘요...)
80페이지까지 넘기며 보고 있었는데 게시글 한번 클릭해 보고 왔더니 1페이지... 최악이잖아요? 다시 넘기기도 귀찮고 그래서
현재 보고 있었던 page 정보는 무조건 계속 다른 곳으로 전달하면서 유지시켜주어야합니다. 그럴려면 어떻게 할까요?
<form ~~>
<input type="hidden" name="page" value="${pageMaker.cri.page}" />
</form>
이런식으로 숨김값으로해서 전달해주는 것이 필요합니다.
그래서 아래 코드에서는
우선, 1) read메서드에서 page와 perPageNum을 전달받을 수 있도록 커맨드 객체를 추가시켜주고, readPage쪽 jsp 페이지에서
페이지에 대한 정보를 사용할 수 있도록 model 객체에 add해주어야 합니다.
2) read.jsp 에서 hidden값으로 값이 넘어갈 수 있도록 작업도 들어가야겠내요.
BoardController의 read()
/views/board/read.jsp
+ [ read.jsp 페이지에서 게시글 정보가 표시되는 부분입니다. ]
<div class="box-body">
<div class="form-group">
<label for="exampleInputEmail1">Title</label> <input type="text"
name='title' class="form-control" value="${boardVO.title}"
readonly="readonly">
</div>
<div class="form-group">
<label for="exampleInputPassword1">Content</label>
<textarea class="form-control" name="content" rows="3"
readonly="readonly">${boardVO.content}</textarea>
</div>
<div class="form-group">
<label for="exampleInputEmail1">Writer</label> <input type="text"
name="writer" class="form-control" value="${boardVO.writer}"
readonly="readonly">
</div>
</div>
<!-- /.box-body -->
<div class="box-footer">
<button type="submit" class="btn btn-warning">수정</button>
<button type="submit" class="btn btn-danger">삭제</button>
<button type="submit" class="btn btn-primary">목록으로</button>
</div>
이 부분을 바로 아래 추가하고
read.jsp 페이지에서 목록가기,수정 등 버튼 클릭에 따른 JQuery 처리
11) 마지막, 수정 페이지에 대한 작업
읽기, 삭제 페이지까지의 작업을 다 했음으로 마지막으로 수정에 관한 작업만 남았습니다.
어차피 위에서 한 작업과 마찬가지로 코드만 추가하도록 하겠습니다. 수정페이지도 read 페이지처럼
hidden으로 데이터를 전송하는 부분이 있겠죠?
- BoardController의 modify 메서드 부분
- /views/board/modify.jsp 페이지 작업
<form role="form" method="post" action="/board/modify"> <!-- action이 없음으로 이 페이지 요청될 때의 url과 동일한 url로 요청하되 post방식으로 됨. -->
<div class="box-body">
<div class="form-group">
<label for="exampleInputEmail1">BNO</label> <input type="text"
name='bno' class="form-control" value="${boardVO.bno}"
readonly="readonly"> <!-- 게시글 번호는 수정하지 못하도록 readonly 로 설정 -->
</div>
<div class="form-group">
<label for="exampleInputEmail1">Title</label> <input type="text"
name='title' class="form-control" value="${boardVO.title}"> <!-- 읽어온 게시글 값들을 미리 세팅해 놔야함 -->
</div>
<div class="form-group">
<label for="exampleInputPassword1">Content</label>
<textarea class="form-control" name="content" rows="3">${boardVO.content}</textarea>
</div>
<div class="form-group">
<label for="exampleInputEmail1">Writer</label>
<input type="text" name="writer" class="form-control" value="${boardVO.writer}">
</div>
<!-- 숨김값으로 보내줘야 할 값들 -->
<input type="hidden" name="page" value="${cri.page }" />
<input type="hidden" name="perPageNum" value="${cri.perPageNum }" />
</div>
<!-- /.box-body -->
</form>
<div class="box-footer">
<button type="submit" class="btn btn-primary">수정</button>
<button type="submit" class="btn btn-warning">취소</button>
</div>
<script>
$(document).ready(function() {
var formObj = $("form[role='form']");
console.log(formObj);
// 취소 클릭시 다시 리스트 목록페이지로 돌아가도록 함
$(".btn-warning").on("click", function() {
// self.location : 현재 페이지를 다른 페이지로 전환할 떄 사용하는 JQuery 메서드
self.location = "/board/listPage?page=${cri.page}&perPageNum=${cri.perPageNum}";
});
// 수정 버튼 클릭시 폼 제출되도록 함
$(".btn-primary").on("click", function() {
formObj.submit();
});
});
</script>
'개발 > 스프링' 카테고리의 다른 글
handlebars js 라이브러리를 이용한 View 표현하기 (1) | 2017.05.31 |
---|---|
sitemesh 추가하기 (0) | 2017.05.11 |
스프링 환경설정 (2) | 2017.05.06 |
쿠키와 세션을 이용한 자동 로그인 처리 (4) | 2017.05.06 |
인터셉터(Interceptor)란? + 인터셉터를 이용한 로그인 처리 (6) | 2017.05.05 |