spring 21강 게시판 만들기2( 페이지 나누기, 검색 기능 )
게시판.sql
delete from board;
--게시물 1000개 입력
declare
i number := 1;
begin
while i<= 1000 loop
insert into board (bno, title, content, writer)
values
( (select nvl( max(bno) +1, 1 ) from board ), '제목' || i , '내용' || i , 'kim' ); -- id는 member테이블에 있는 id로 입력해야함
i := i+1;
end loop;
end;
/
*|| : 오라클에서는 + 연산자
페이지 나누는 쿼리
select * from(
select rownum as rn, A.* from ( // 괄호 안 A테이블에 다시 rownum을 붙인다. (A테이블에서는 rownum왜 붙이는거지)
select rownum, bno, title, content, b.regdate, viewcnt, usename -- (3)
from board b, tbl_member m -- (1)
where b.writer = m.userid -- (2)
order by bno desc, regdate desc -- (마지막)
) A
) where rn between 41 and 50; // 10페이지씩 불러오기
rownum은 sql을 실행한 후에 번호를 붙임
service.board/ Pager.java
페이지 나누기
- 페이지당 게시물수 : 10개
- 전체 게시물수 : 991개
- 몇 페이지? : 100
991 / 10 => 99.1 올림 => 100페이지
- 페이지의 시작번호, 끝번호
1 |
시작 번호( (현재페이지 -1) * 페이지당 게시물 수(10) + 1 ) //pageBegin |
.. |
... |
.. |
... |
10 |
끝 번호 ( 시작번호 + 페이지당 게시물 수 -1 )//pageEnd |
where rn between 1 and 10
1페이지 => 1 ~ 10
2페이지 => 11 ~ 20
...
시작 글번호 = (현재페이지 -1) * 페이지당 게시물 수(10) + 1
끝 글번호 = 시작번호 + 페이지당 게시물 수 -1
ex) 현재 6페이지 => 50+1 = 51번이 시작번호
51+10-1 = 60번이 끝번호
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] |
-전체 페이지 블록 □ 수
전체페이지 갯수 ( 91( [ n ] ) ) / 10 (소수점 이하 올림) => 10
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] |
× 10
-현재 페이지가 속한 블록
(현재 페이지 -1) / 페이지 블록 단위 + 1
-페이지 블록의 시작 번호
( 현재 블록 - 1 ) *10 +1
service / Pager.java
PAGE_SCALE | 페이지당 게시물 수 |
|
BLOCK_SCALE | 화면당 페이지 수 |
|
curPage |
현재 페이지 | ( 글번호 - 1 ) / PAGE_SCALE + 1 |
prevPage |
이전 페이지 | [이전]을 눌렀을 때 이동할 페이지 번호 |
nextPage |
다음 페이지 | [다음]을 눌렀을 때 이동할 페이지 번호 |
totPage |
전체 페이지 개수 | 글 개수 / PAGE_SCALE + ( 글 개수 % PAGE_SCALE ==0 ? 0 : 1 ) |
totBlock |
전체 페이지 블록 개수 | totPage / BLOCK_SCALE => 소수점 이하 올림 |
curBlock |
현재 페이지 블록 | 현재 페이지 - 1 / BLOCK_SCALE + 1 |
prevBlock |
이전 페이지 블록 | |
nextBlock |
다음 페이지 블록 | // where rn between #{start} and #{end} |
pageBegin |
#{start} | |
pageEnd |
#{end} | |
blcokBegin |
현재블록의 시작번호 | |
blockEnd |
현재 블록 끝번호 |
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 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | package com.example.spring02.service.board; public class Pager { 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; //다음 블록 private int pageBegin; // #{start} 변수에 전달될 값 private int pageEnd; // #{end} 변수에 전달될 값 private int blockBegin; //블록의 시작페이지 번호 private int blockEnd; //블록의 끝페이지 번호 //생성자 // Pager(레코드갯수, 출력할페이지번호) public Pager(int count, int curPage) { curBlock = 1; //현재블록 번호 this.curPage = curPage; //현재 페이지 번호 setTotPage(count); //전체 페이지 갯수 계산 setPageRange(); // #{start}, #{end} 값 계산 setTotBlock(); // 전체 블록 갯수 계산 setBlockRange(); //블록의 시작,끝 번호 계산 } //블록의 시작, 끝 번호 계산 ( (현재페이지-1) /10 +1 ) public void setBlockRange() { //원하는 페이지가 몇번째 블록에 속하는지 계산 curBlock=(curPage-1)/BLOCK_SCALE + 1; //블록의 시작페이지,끝페이지 번호 계산 blockBegin=(curBlock-1)*BLOCK_SCALE+1; blockEnd=blockBegin+BLOCK_SCALE-1; //마지막 블록 번호가 범위를 초과하지 않도록 처리 if(blockEnd > totPage) { blockEnd = totPage; } //[이전][다음]을 눌렀을 때 이동할 페이지 번호 //첫번째 페이지블록이면 1, 아니면 전 블록 마지막 페이지 prevPage=(curBlock==1) ? 1 : (curBlock-1)*BLOCK_SCALE; //현재 블록이 총블록 수보다 작거나 같으면 (현재블록*10 +1) //= 다음블록 첫페이지 //?? 현재블록이 총블록보다 큰 경우는 어떤 경우? nextPage=curBlock>totBlock ? (curBlock*BLOCK_SCALE) : (curBlock*BLOCK_SCALE)+1; //마지막 페이지가 범위를 초과하지 않도록 처리 //다음페이지가 마지막페이지보다 크거나 같을 경우 다음페이지 = 마지막페이지 if(nextPage >= totPage) { nextPage=totPage; } } //블록의 갯수 계산 public void setTotBlock() { totBlock = (int)Math.ceil(totPage*1.0 / BLOCK_SCALE); } // where rn between #{start} and #{end}에 입력될 값 public void setPageRange() { // 시작번호=(현재페이지-1)x페이지당 게시물수 + 1 // 끝번호=시작번호 + 페이지당 게시물수 - 1 pageBegin = (curPage-1) * PAGE_SCALE + 1; pageEnd = pageBegin + PAGE_SCALE - 1; } ...GETTER / SETTER 코드 생략 ... //전체 페이지 갯수 계산 public void setTotPage(int count) { // Math.ceil() 올림 totPage = (int)Math.ceil(count*1.0 / PAGE_SCALE); } } |
BoardController.java의 list.do 수정
레코드 개수 구하기 > 페이징 객체로 시작 번호, 끝 번호 구하기 > BoardService listAll에 전달
( list.jsp의 list(page) 함수에서 넘어온 )curPage를 GET방식 파라미터로 받아오고 defaultValue=1 로 설정해준다.
search_option과 keyword는 POST방식의 폼데이터로 넘어오지만
개별값으로 받아서 쓰는것이 편하므로 @RequestParam으로 받는다.
search_option은 defaultValue="all" / keyword는 defaultValue="" 빈값으로 설정
서비스에 검색옵션과 키워드를 주고 레코드 개수를 세도록 처리를 넘기고 그 값을 count 변수에 담는다.
페이징 처리하는 객체 Pager의 생성자에 레코드 개수, 현재 페이지를 주어 페이징처리 객체를 생성한다.
pager에서 시작 번호/ 끝번호를 받아온다
서비스의 listAll ( 검색옵션, 키워드, 시작번호, 끝번호 )로 데이터를 받아와
(이 시작번호/끝번호가 Mapper의 where rn between #{start} and #{end} 에 들어간다 )
BoardDAO타입 List에 처리한 데이터를 담아준다.
>> map에 리스트, 글개수, 페이징 객체, 검색 옵션, 키워드를 담고 mav에 담아 포워딩
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 | @RequestMapping("list.do") //세부적인 url pattern public ModelAndView list( @RequestParam(defaultValue="name") String search_option, @RequestParam(defaultValue="") String keyword, @RequestParam(defaultValue="1") int curPage) throws Exception{ //레코드 갯수 계산 int count= boardService.countArticle(search_option,keyword); //페이지 관련 설정 Pager pager=new Pager(count, curPage); int start=pager.getPageBegin(); int end=pager.getPageEnd(); List<BoardDTO> list= boardService.listAll(search_option,keyword,start,end); //게시물 목록 ModelAndView mav=new ModelAndView(); HashMap<String,Object> map=new HashMap<>(); map.put("list", list); //map에 자료 저장 map.put("count", count); map.put("pager", pager); //페이지 네비게이션을 위한 변수 map.put("search_option", search_option); map.put("keyword",keyword); mav.setViewName("board/list"); //포워딩할 뷰의 이름 mav.addObject("map", map); //ModelAndView에 map을 저장 return mav; // board/list.jsp로 이동 } |
BoardDAOImpl.java [ countArticle (게시글 개수 구하는 메서드) ]
1 2 3 4 5 6 7 8 9 | //레코드 갯수 계산 @Override public int countArticle( String search_option, String keyword) throws Exception { Map<String,String> map=new HashMap<>(); map.put("search_option", search_option); map.put("keyword", "%"+keyword+"%"); return sqlSession.selectOne("board.countArticle",map); } | cs |
sql에 여러가지 변수를 전달해야할 경우엔 DAO에서 HashMap에 묶어보낸다.
1 2 3 4 5 6 7 8 9 10 11 12 | //게시물 목록 리턴 @Override public List<BoardDTO> listAll(String search_option, String keyword,int start, int end) throws Exception { Map<String,Object> map=new HashMap<>(); map.put("search_option", search_option); map.put("keyword", "%"+keyword+"%"); map.put("start", start); //맵에 자료 저장 map.put("end", end); // mapper에는 2개 이상의 값을 전달할 수 없음(dto 또는 map 사용) return sqlSession.selectList("board.listAll",map); } | cs |
boardMapper.xml
1 2 3 4 5 6 | <!-- 레코드 갯수 계산 --> <select id="countArticle" resultType="int"> select count(*) from board b,member m <include refid="search" /> </select> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <select id="listAll" resultType="com.example.spring02.model.board.dto.BoardDTO"> <include refid="paging_header" /> select bno,title,writer,name,regdate,viewcnt,show ,(select count(*) from reply where bno=b.bno) cnt from board b, member m <include refid="search" /> order by bno desc <include refid="paging_footer" /> </select> <sql id="paging_header"> select * from ( select rownum as rn, A.* from ( </sql> <sql id="paging_footer"> ) A ) where rn between #{start} and #{end} </sql> |
<sql> 태그는 sql을 재활용할 수 있게 해준다.
페이지 나누기할때 header와 footer는 항상 똑같기때문에 코드를 재활용할 수 있게 쓴다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <sql id="search"> <choose> <when test="search_option != 'all' "> where b.writer=m.userid and show='Y' and ${search_option} like #{keyword} </when> <otherwise> where b.writer=m.userid and show='Y' and (name like #{keyword} or title like #{keyword} or content like #{keyword} ) </otherwise> </choose> </sql> |
board/list.jsp 페이지 네비게이션 출력 부분
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 | <!-- 페이지 네비게이션 출력 --> <tr> <td colspan="5" align="center"> <c:if test="${map.pager.curBlock > 1}"> <a href="#" onclick="list('1')">[처음]</a> </c:if> <c:if test="${map.pager.curBlock > 1}"> <a href="#" onclick="list('${map.pager.prevPage}')"> [이전]</a> </c:if> <c:forEach var="num" begin="${map.pager.blockBegin}" end="${map.pager.blockEnd}"> <c:choose> <c:when test="${num == map.pager.curPage}"> <!-- 현재 페이지인 경우 하이퍼링크 제거 --> <span style="color:red;">${num}</span> </c:when> <c:otherwise> <a href="#" onclick="list('${num}')">${num}</a> </c:otherwise> </c:choose> </c:forEach> <c:if test="${map.pager.curBlock < map.pager.totBlock}"> <a href="#" onclick="list('${map.pager.nextPage}')">[다음]</a> </c:if> <c:if test="${map.pager.curPage < map.pager.totPage}"> <a href="#" onclick="list('${map.pager.totPage}')">[끝]</a> </c:if> </td> </tr> |
1 2 3 4 5 6 7 8 9 10 11 12 13 | <script> $(function(){ //검색기능 $("#btnWrite").click(function(){ location.href="${path}/board/write.do"; }); }); function list(page){ location.href="${path}/board/list.do?curPage="+page; } </script> |
list(page) 함수는 원하는 num 페이지로 이동시켜준다.
검색기능
list.jsp 검색폼 부분
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <!-- 검색폼 --> <form name="form1" method="post" action="${path}/board/list.do"> <select name="search_option"> <option value="name" <c:if test="${map.search_option == 'name'}">selected</c:if> >이름</option> <option value="title" <c:if test="${map.search_option == 'title'}">selected</c:if> >제목</option> <option value="content" <c:if test="${map.search_option == 'content'}">selected</c:if> >내용</option> <option value="all" <c:if test="${map.search_option == 'all'}">selected</c:if> >이름+내용+제목</option> </select> <input name="keyword" value="${map.keyword}"> <input type="submit" value="조회"> </form> |
옵션태그 안에서 selected 설정 => 검색 시 [ 제목 ]을 선택하고 검색하면 이 선택이 검색 후에도 고정됨.
브라우저에서 소스코드 확인 : <option value="title" selected>제목</option>
Controller에 넘어갈때 search_option의 defaultValue는 "all"이고 keyword의 defaultValue는 ""
'Spring > study' 카테고리의 다른 글
spring 22강 게시판 만들기3( 상세 화면, 댓글 쓰기/댓글 목록/댓글 갯수) (0) | 2019.07.04 |
---|---|
selectOne / selectList 쓰임 차이 (0) | 2019.07.04 |
spring 20강 게시판 만들기1(목록, 글쓰기) (0) | 2019.07.03 |
spring 19강 Smart Editor(CKEditor, SummerNote) (0) | 2019.07.03 |
Spring Framework root-context.xml과 servlet-context.xml의 차이점 (0) | 2019.07.03 |