728x90
Spring - 게시판 만들기 연습(페이징 처리)
1. 실행화면
01) 게시글 목록의 페이지, 다음, 이전, 처음, 끝 버튼 생성, 현재페이지 하이퍼링크 하이퍼링크제거
02) 게시글 검색 후 페이지 이동시(끝페이지 이동, 상세화면으로 이동) 검색 내용이 손실되지 않게 처리
2. 소스코드
01) Controller(흐름제어)
BoardController
// 01. 게시글 목록 @RequestMapping("list.do") // @RequestParam(defaultValue="") ==> 기본값 할당 : 현재페이지를 1로 초기화 public ModelAndView list(@RequestParam(defaultValue="title") String searchOption, @RequestParam(defaultValue="") String keyword, @RequestParam(defaultValue="1") int curPage) throws Exception{ // 레코드의 갯수 계산 int count = boardService.countArticle(searchOption, keyword); // 페이지 나누기 관련 처리 BoardPager boardPager = new BoardPager(count, curPage); int start = boardPager.getPageBegin(); int end = boardPager.getPageEnd(); List<BoardVO> list = boardService.listAll(start, end, searchOption, keyword); // 데이터를 맵에 저장 Map<String, Object> map = new HashMap<String, Object>(); map.put("list", list); // list map.put("count", count); // 레코드의 갯수 map.put("searchOption", searchOption); // 검색옵션 map.put("keyword", keyword); // 검색키워드 map.put("boardPager", boardPager); ModelAndView mav = new ModelAndView(); mav.addObject("map", map); // 맵에 저장된 데이터를 mav에 저장 mav.setViewName("board/list"); // 뷰를 list.jsp로 설정 return mav; // list.jsp로 List가 전달된다. } | cs |
02) Service(비지니스로직, DB연동 이외의 작업처리)
BoardPager(페이지 나누기 관련 작업 클래스)
package com.example.spring02.service.board; public class BoardPager { // 페이지당 게시물 수 public static final int PAGE_SCALE = 10; // 화면당 페이지 수 public static final int BLOCK_SCALE = 10; private int curPage; // 현재 페이수 private int prevPage; // 이전 페이지 private int nextPage; // 다음 페이지 private int totPage; // 전체 페이지 갯수 private int totBlock; // 전체 페이지 블록 갯수 private int curBlock; // 현재 페이지 블록 private int prevBlock; // 이전 페이지 블록 private int nextBlock; // 다음 페이지 블록 // WHERE rn BETWEEN #{start} AND #{end} private int pageBegin; // #{start} private int pageEnd; // #{end} // [이전] blockBegin -> 41 42 43 44 45 46 47 48 49 50 [다음] private int blockBegin; // 현재 페이지 블록의 시작번호 // [이전] 41 42 43 44 45 46 47 48 49 50 <- blockEnd [다음] private int blockEnd; // 현재 페이지 블록의 끝번호 // 생성자 // BoardPager(레코드 갯수, 현재 페이지 번호) public BoardPager(int count, int curPage){ curBlock = 1; // 현재 페이지 블록 번호 this.curPage = curPage; // 현재 페이지 설정 setTotPage(count); // 전체 페이지 갯수 계산 setPageRange(); // setTotBlock(); // 전체 페이지 블록 갯수 계산 setBlockRange(); // 페이지 블록의 시작, 끝 번호 계산 } public void setBlockRange(){ // *현재 페이지가 몇번째 페이지 블록에 속하는지 계산 curBlock = (int)Math.ceil((curPage-1) / BLOCK_SCALE)+1; // *현재 페이지 블록의 시작, 끝 번호 계산 blockBegin = (curBlock-1)*BLOCK_SCALE+1; // 페이지 블록의 끝번호 blockEnd = blockBegin+BLOCK_SCALE-1; // *마지막 블록이 범위를 초과하지 않도록 계산 if(blockEnd > totPage) blockEnd = totPage; // *이전을 눌렀을 때 이동할 페이지 번호 prevPage = (curPage == 1)? 1:(curBlock-1)*BLOCK_SCALE; // *다음을 눌렀을 때 이동할 페이지 번호 nextPage = curBlock > totBlock ? (curBlock*BLOCK_SCALE) : (curBlock*BLOCK_SCALE)+1; // 마지막 페이지가 범위를 초과하지 않도록 처리 if(nextPage >= totPage) nextPage = totPage; } public void setPageRange(){ // WHERE rn BETWEEN #{start} AND #{end} // 시작번호 = (현재페이지-1)*페이지당 게시물수 +1 pageBegin = (curPage-1)*PAGE_SCALE+1; // 끝번호 = 시작번호+페이지당 게시물수 -1 pageEnd = pageBegin+PAGE_SCALE-1; } // Getter/Setter public int getCurPage() { return curPage; } public void setCurPage(int curPage) { this.curPage = curPage; } public int getPrevPage() { return prevPage; } public void setPrevPage(int prevPage) { this.prevPage = prevPage; } public int getNextPage() { return nextPage; } public void setNextPage(int nextPage) { this.nextPage = nextPage; } public int getTotPage() { return totPage; } public void setTotPage(int count) { // Math.ceil(실수) 올림 처리 totPage = (int) Math.ceil(count*1.0 / PAGE_SCALE); } public int getTotBlock() { return totBlock; } // 페이지 블록의 갯수 계산(총 100페이지라면 10개의 블록) public void setTotBlock() { // 전체 페이지 갯수 / 10 // 91 / 10 => 9.1 => 10개 totBlock = (int)Math.ceil(totPage / BLOCK_SCALE); } public int getCurBlock() { return curBlock; } public void setCurBlock(int curBlock) { this.curBlock = curBlock; } public int getPrevBlock() { return prevBlock; } public void setPrevBlock(int prevBlock) { this.prevBlock = prevBlock; } public int getNextBlock() { return nextBlock; } public void setNextBlock(int nextBlock) { this.nextBlock = nextBlock; } public int getPageBegin() { return pageBegin; } public void setPageBegin(int pageBegin) { this.pageBegin = pageBegin; } public int getPageEnd() { return pageEnd; } public void setPageEnd(int pageEnd) { this.pageEnd = pageEnd; } public int getBlockBegin() { return blockBegin; } public void setBlockBegin(int blockBegin) { this.blockBegin = blockBegin; } public int getBlockEnd() { return blockEnd; } public void setBlockEnd(int blockEnd) { this.blockEnd = blockEnd; } } | cs |
BoardService(인터페이스) - 매개변수 int start, int end 추가
// 05. 게시글 전체 목록 public List<BoardVO> listAll(int start, int end, String searchOption, String keyword) throws Exception; | cs |
BoardServiceImpl(인터페이스 구현 클래스)
// 05. 게시글 전체 목록 @Override public List<BoardVO> listAll(int start, int end, String searchOption, String keyword) throws Exception { return boardDao.listAll(start, end, searchOption, keyword); } | cs |
03) Model(비지니스로직, DB연동 작업처리)
BoardDAO(인터페이스)
// 05. 게시글 전체 목록 public List<BoardVO> listAll(int start, int end, String searchOption, String keyword) throws Exception; | cs |
BoardDAOImpl(인터페이스 구현 클래스)
// 05. 게시글 전체 목록 @Override public List<BoardVO> listAll(int start, int end, String searchOption, String keyword) throws Exception { // 검색옵션, 키워드 맵에 저장 Map<String, Object> map = new HashMap<String, Object>(); map.put("searchOption", searchOption); map.put("keyword", keyword); // BETWEEN #{start}, #{end}에 입력될 값을 맵에 map.put("start", start); map.put("end", end); return SqlSession.selectList("board.listAll", map); } | cs |
boardMapper.xml
<!-- 게시글 전체 목록 조회 및 검색조회까지 --> <select id="listAll" resultType="com.example.spring02.model.board.dto.BoardVO"> <!-- 목록 조회 및 페이징 전체 쿼리 --> <include refid="pagingHeader"></include> SELECT rownum, bno, title, content, b.regdate, viewcnt, user_name as userName FROM tbl_board b, tbl_member m <include refid="search"></include> ORDER BY bno DESC, b.regdate DESC <include refid="pagingFooter"></include> </select> <!-- 검색 조건 sql --> <sql id="search"> <choose> <!-- 검색옵션이 전체 검색일 경우 --> <when test="searchOption == 'all'"> WHERE b.writer = m.user_id AND (user_name like '%'||#{keyword}||'%' OR content like '%'||#{keyword}||'%' OR title like '%'||#{keyword}||'%') </when> <!-- 전체 검색이 아닐 경우 --> <otherwise> WHERE b.writer = m.user_id AND ${searchOption} like '%'||#{keyword}||'%' </otherwise> </choose> </sql> <!-- 페이징 sql --> <sql id="pagingHeader"> SELECT * FROM ( SELECT ROWNUM AS rn, A.* FROM ( </sql> <sql id="pagingFooter"> ) A ) WHERE rn BETWEEN #{start} AND #{end} </sql> | cs |
04) View(화면)
list.jsp(게시글 목록)
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>게시글 목록</title> <%@ include file="../include/header.jsp" %> <script> $(document).ready(function(){ $("#btnWrite").click(function(){ // 페이지 주소 변경(이동) location.href = "${path}/board/write.do"; }); }); // **원하는 페이지로 이동시 검색조건, 키워드 값을 유지하기 위해 function list(page){ location.href="${path}/board/list.do?curPage="+page+"&searchOption-${map.searchOption}"+"&keyword=${map.keyword}"; } </script> </head> <body> <%@ include file="../include/menu.jsp" %> <h2>게시글 목록</h2> <form name="form1" method="post" action="${path}/board/list.do"> <select name="searchOption"> <!-- 검색조건을 검색처리후 결과화면에 보여주기위해 c:out 출력태그 사용, 삼항연산자 --> <option value="all" <c:out value="${map.searchOption == 'all'?'selected':''}"/> >제목+이름+제목</option> <option value="user_name" <c:out value="${map.searchOption == 'user_name'?'selected':''}"/> >이름</option> <option value="content" <c:out value="${map.searchOption == 'content'?'selected':''}"/> >내용</option> <option value="title" <c:out value="${map.searchOption == 'title'?'selected':''}"/> >제목</option> </select> <input name="keyword" value="${map.keyword}"> <input type="submit" value="조회"> <!-- 로그인한 사용자만 글쓰기 버튼을 활성화 --> <c:if test="${sessionScope.userId != null}"> <button type="button" id="btnWrite">글쓰기</button> </c:if> </form> <!-- 레코드의 갯수를 출력 --> ${map.count}개의 게시물이 있습니다. <table border="1" width="600px"> <tr> <th>번호</th> <th>제목</th> <th>이름</th> <th>작성일</th> <th>조회수</th> </tr> <c:forEach var="row" items="${map.list}"> <tr> <td>${row.bno}</td> <!-- ** 게시글 상세보기 페이지로 이동시 게시글 목록페이지에 있는 검색조건, 키워드, 현재페이지 값을 유지하기 위해 --> <td><a href="${path}/board/view.do?bno=${row.bno}&curPage=${map.boardPager.curPage}&searchOption=${map.searchOption}&keyword=${map.keyword}">${row.title}</a></td> <td>${row.userName}</td> <td> <!-- 원하는 날짜형식으로 출력하기 위해 fmt태그 사용 --> <fmt:formatDate value="${row.regdate}" pattern="yyyy-MM-dd HH:mm:ss"/> </td> <td>${row.viewcnt}</td> </tr> </c:forEach> <tr> <td colspan="5"> <!-- **처음페이지로 이동 : 현재 페이지가 1보다 크면 [처음]하이퍼링크를 화면에 출력--> <c:if test="${map.boardPager.curBlock > 1}"> <a href="javascript:list('1')">[처음]</a> </c:if> <!-- **이전페이지 블록으로 이동 : 현재 페이지 블럭이 1보다 크면 [이전]하이퍼링크를 화면에 출력 --> <c:if test="${map.boardPager.curBlock > 1}"> <a href="javascript:list('${map.boardPager.prevPage}')">[이전]</a> </c:if> <!-- **하나의 블럭에서 반복문 수행 시작페이지부터 끝페이지까지 --> <c:forEach var="num" begin="${map.boardPager.blockBegin}" end="${map.boardPager.blockEnd}"> <!-- **현재페이지이면 하이퍼링크 제거 --> <c:choose> <c:when test="${num == map.boardPager.curPage}"> <span style="color: red">${num}</span> </c:when> <c:otherwise> <a href="javascript:list('${num}')">${num}</a> </c:otherwise> </c:choose> </c:forEach> <!-- **다음페이지 블록으로 이동 : 현재 페이지 블럭이 전체 페이지 블럭보다 작거나 같으면 [다음]하이퍼링크를 화면에 출력 --> <c:if test="${map.boardPager.curBlock <= map.boardPager.totBlock}"> <a href="javascript:list('${map.boardPager.nextPage}')">[다음]</a> </c:if> <!-- **끝페이지로 이동 : 현재 페이지가 전체 페이지보다 작거나 같으면 [끝]하이퍼링크를 화면에 출력 --> <c:if test="${map.boardPager.curPage <= map.boardPager.totPage}"> <a href="javascript:list('${map.boardPager.totPage}')">[끝]</a> </c:if> </td> </tr> </table> </body> </html> | cs |
view.jsp(게시글 상세보기)
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>게시글 작성</title> <%@ include file="../include/header.jsp" %> <script> $(document).ready(function(){ // ** 목록 버튼 클릭 이벤트 : 버튼 클릭시 상세보기화면에 있던 페이지, 검색옵션, 키워드 값을 가지로 목록으로 이동 $("#btnList").click(function(){ location.href="${path}/board/list.do?curPage=${curPage}&searchOption=${searchOption}&keyword=${keyword}"; }); $("#btnDelete").click(function(){ if(confirm("삭제하시겠습니까?")){ document.form1.action = "${path}/board/delete.do"; document.form1.submit(); } }); $("#btnUpdete").click(function(){ //var title = document.form1.title.value; ==> name속성으로 처리할 경우 //var content = document.form1.content.value; //var writer = document.form1.writer.value; var title = $("#title").val(); var content = $("#content").val(); //var writer = $("#writer").val(); if(title == ""){ alert("제목을 입력하세요"); document.form1.title.focus(); return; } if(content == ""){ alert("내용을 입력하세요"); document.form1.content.focus(); return; } /* if(writer == ""){ alert("이름을 입력하세요"); document.form1.writer.focus(); return; } */ document.form1.action="${path}/board/update.do" // 폼에 입력한 데이터를 서버로 전송 document.form1.submit(); }); }); </script> </head> <body> <%@ include file="../include/menu.jsp" %> <h2>게시글 보기</h2> <form name="form1" method="post"> <div> <!-- 원하는 날짜형식으로 출력하기 위해 fmt태그 사용 --> 작성일자 : <fmt:formatDate value="${dto.regdate}" pattern="yyyy-MM-dd a HH:mm:ss"/> <!-- 날짜 형식 => yyyy 4자리연도, MM 월, dd 일, a 오전/오후, HH 24시간제, hh 12시간제, mm 분, ss 초 --> </div> <div> 조회수 : ${dto.viewcnt} </div> <div> 제목 <input name="title" id="title" size="80" value="${dto.title}" placeholder="제목을 입력해주세요"> </div> <div> 내용 <textarea name="content" id="content" rows="4" cols="80" placeholder="내용을 입력해주세요">${dto.content}</textarea> </div> <div> 이름 <%-- <input name="writer" id="writer" value="${dto.writer}" placeholder="이름을 입력해주세요"> --%> ${dto.userName} </div> <div style="width:650px; text-align: center;"> <!-- 게시물번호를 hidden으로 처리 --> <input type="hidden" name="bno" value="${dto.bno}"> <!-- 본인이 쓴 게시물만 수정, 삭제가 가능하도록 처리 --> <c:if test="${sessionScope.userId == dto.writer}"> <button type="button" id="btnUpdete">수정</button> <button type="button" id="btnDelete">삭제</button> </c:if> <!-- **상세보기 화면에서 게시글 목록화면으로 이동 --> <button type="button" id="btnList">목록</button> </div> </form> </body> </html> | cs |