스프링

 

페이징 처리에는 반드시 다음과 같은 원칙이 지켜져야만 한다.

 

- 페이징 처리는 반드시 GET 방식만을 이용해서 처리한다.

- 페이지는 다른 사람에게 URL 로 전달하는 경우가 많기 때문에, 반드시 GET 방식으로만 처리 된다.

- 페이징 처리가 되면 조회 화면에서 반드시 '목록보기'가 필요하다.

- 목록 페이지에서 3페이지를 보다가 특정 게시물을 보았다면, 다시 '목록가기' 버튼을 눌러서 다시 목록에서 3페이지로 이동하는

기능이 구현되어야 한다.

- 페이징 처리에는 반드시 필요한 페이지 번호만을 제공한다.

- 예컨데, 한 페이지에 10 개씩 데이터를 출력하는 경우라면, 전체 데이터가 32건이 있을 때에는 4페이지까지만 화면에 출력돼야 한다.

만일 더 많은 데이터가 있을 때에는 적당한 수의 페이지번호를 출력하고, 뒤로 가는 화살표 등을 이용해서 보여줘야 한다.

 

 

 

페이징 처리의 개발 순서는 크게 다음과 같다.

 

1. 단순히 페이지 데이터가 화면에 출력하는 작업

2. 화면 하단에 페이지 번호가 표시되고, 이를 클릭할 경우 제대로 이동하는 작업

3. 조회 페이지에서 목록가기를 선택하면 보던 페이지 정보를 유지한 채로 이동하는 작업

 

 

1. MYSQL 페이징 처리 방법 및 소스  

 

1page limit 0, 10

2page limit 10, 10

 

충분한 데이터 넣기  자가 복사 (self copy) 하는 형태로 데이터를 많이 넣어주도록 한다.

 

1 . 자가 복제 방법

INSERT INTO tbl_board ( title, content, writer) ( SELECT title, content, writer FROM tbl_board );

 

활용

INSERT INTO tbl_member (userid, userpw, username, email, regdate, updatedate, upoint) 

 (SELECT ( CONCAT(userid, (select count(*)+1 from tbl_member )) ) , userpw, username, 
 
 email, regdate, updatedate, upoint FROM tbl_member ) ;

 

 

 2. 더미데이터 생성기로 데이터를 넣는 방법

http://www.mockaroo.com/

 

날짜의 경우 타입이 맞지가 않아 에러가 발생해서 컬럼에서 날짜를 드럽 시킨후 임포트 하였다.

그리고 다시 날짜 컬럼을 생성 하는 방식을 취한다.

-- 페이징 처리를 위해 다음과 같이 변경한다.

create table tbl_member(
	mid int  AUTO_INCREMENT primary key ,
	userid varchar(100)  ,
	userpw varchar(100) not null,
	username varchar(50) not null,
	email varchar(100)
   

);

-- 더미데이터를 삽입 한다.

-- 컬럼을 변경한다.

alter table tbl_member add COLUMN  regdate timestamp default now();

alter table tbl_member add COLUMN  updatedate timestamp default now();
--  tbl_member 포인트 컬럼 추가
alter table tbl_member add COLUMN ( upoint int not null default 0);


 

 

 

더미데이터 생성기로 천개의 데이터를 추출하였다.

CSV import 

 

자바빈

public class MemberVO {
	private int mid;
	private String userid ; 
	private String userpw ; 
	private String username;
	private String email ;
	private int upoint;
    private Timestamp regdate; 
    private Timestamp updatedate;

 getter, setter

 

페이징  SQL 

	<!-- 	회원 목록 출력 -->
	<select id="readListMember" resultType="net.macaronics.web.domain.MemberVO">
		
		<![CDATA[
			
		   select mid, userid, userpw, username, email, regdate, updatedate, upoint 
		   
		    from TBL_MEMBER where mid >=0 order by mid desc 
		    
		    limit #{pageStart}, #{perPageNum}
		
		
		]]>
		 
	</select>

 

 

  1. 단계   

package config.paging;
//MySQL PageMaker
public class PageMaker {
	//mysql 들어갈 값은 page 가 아니라 pageStart, perPageNum 이다.
	//limit #{pageStart}, #{perPageNum}
	private int page;
	private int perPageNum;
	private int pageStart;
	
	public PageMaker() {
		this.page=1;          //초기 페이지는 1 
		this.perPageNum=10;  //limit 10 개씩 보여준다.
	}
	
	
	public void setPage(int page) {
		//페이지 번호가 0이거나 0보다 작으면 1페이지로 한다.
		//
		if(page <=0){
			this.page=1;
			return;
		}
		this.page = page;
	}
		
	// MyBatis SQL 의 Mapper 에서 인식해서 가져가는 파라미터 값 메소드 #{perPageNum}
	public void setPerPageNum(int perPageNum) {
		//몇개 씩 보여줄것인가 이다. 최대 100개씩 보여 줄것으로 설정한다.
		//만약 0보다 작거나 100 보다 크면 10으로 초기화 시킨다.
		if(perPageNum <=0 || perPageNum >100){
			this.perPageNum=10;
			return;
		}
		this.perPageNum = perPageNum;
	}


	public int getPage() {
		return page;
	}



	public int getPerPageNum() {
		return perPageNum;
	}


	// MyBatis SQL 의 Mapper 에서 인식해서 가져가는 파라미터 값 메소드 #{pageStart}
	public int getPageStart() {
		//실질적으로 Mybatis 에서  파라미터로 인식해서  가져오는 것은 get 이다.
		// 따라서 getPageStart 에서 값을 설정한다.
		//시작 데이터 번호 = (페이지 번호 -1 ) * 페이지당 보여지는 개수
		this.pageStart=(this.page -1)*perPageNum;
		return this.pageStart;
	}


	public void setPageStart(int pageStart) {
		this.pageStart = pageStart;
	}
	
	
	
	
}

 

 

1차 테스트를 진행한다.

@Repository
public class MemberDAOImpl implements MemberDAO {



	private static final Logger log = LoggerFactory.getLogger(MemberDAOImpl.class);

	// private static final String
	// namespace="net.macaronics.mapper.o.memberMapper.";
	private static final String namespace = "net.macaronics.mapper.memberMapper.";

       	// 회원 목록 출력
	@Override
	public List<MemberVO> readListMember(PageMaker pageMaker) {
		return sqlSession.selectList(namespace + "readListMember", pageMaker);
	}



}

 

	@Test
	public void testReadListMember() {
		PageMaker pageMaker =new PageMaker();
		pageMaker.setPage(2);
		pageMaker.setPerPageNum(20);
		
		List<MemberVO> list =dao.readListMember(pageMaker);
		for(MemberVO member : list){
			log.info("testReadListMember - {},  {}" ,  member.getMid() ,member.getUserid());
		}
		
	}

 

 

 

  2 단계   

-  시작 페이지 번호(startPage)  - 예를 들어 화면상에 10 개의 페이지 번호를 출력한다고 했을 때 , 현재 페이지가 1에서 10의 사이에

있는 번호라면 시작 페이지는 1이 되어야 한다.

 

- 끝 페이지 번호 (endPage) - 시작 페이지 번호부터 몇 개의 번호를 보여줘야 하는지를 결정해야 한다.

예를 들어 전체 데이터가 65개이고, 현재 페이지가 5페이지라면 시작 페이지 번호는 1이고, 끝 페이지 번호는 7(65개이므로 7페이까지는

표시 ) 돼어야 합니다.

 

- 전체 데이터의 개수(totalCount) - 끝 페이지 번호를 계산할 때 전체 데이터의 개수를 이용해서 최종적으로 끝 페이지의 번호가 결정된다.

 

- 이전 페이지 링크(prev)  - 맨 앞의 페이지 번호가 1이 아니라면 화면상에 링크를 통해서 이전 페이지를 조회할 수 있어야 한다.

 

- 이후 페이지 링크(next) - 맨 뒤의 페이지 이후에 더 많은 데이터가 존재한는 경우 이동이 가능하도록 링크.

 

 

◆ endPage  구하기

흔히들 처음에 구해야 하는 것이 startPage라고 생각하지만, endPage 를 구하는 것이 산술적인 계산이 더 편할 수 있다.

endPage 는 현재의 페이지 번호를 기준으로 계산한다.

 

         //현재 페이지 번호 / 하단 페이지번호 수
         
endPage=(int)(Math.ceil(page / (double)displayPageNum)*displayPageNum);

        

 

- 현재 페이지가 3일 때 : Math.ceil(3/10)*10 =10

- 현재 페이지가 1 일때  : Math.ceil(1/10) * 10 =10

- 현재 페이지가 20일때 : Math.ceil(20/10) * 10 =20

- 현재 페이지가 21 일때  : Math.ceil(21/10)*10 =30

 

◆ startPage 구하기

endPage가 구해졌다면 startPage 는 단순한 빼기의 문제이다.   endPage 가 20 이라면 startPage 는 11 이 된다. 

이 작업은 다음과 같이 계산된다.

 

       startPage=(endPage - displayPageNum) +1;

 

displayPageNum 은 화면에 보여지는 페이지 번호의 수이므로 화면에 10개씩 페이지 번호가 출력되는 경우 endPage에서 9를 뺀 결과가

된다.

 

 

 

◆ totalCount endPage 의 재계산

endPage 는 실제 데이터의 개수와 관련이 있기 때문에 다시 한 번 계산할 필요가 있습니다.  이때 필요한 것은 다음과 같다.

totalCount : 전체 데이터의 개수

perPageNum : 한 번에 보여지는 데이터 수

 

예를 들어 100 개의 데이터 (totalCount) 를 10개씩 보여준다면 (perPageNum) , endPage 는 10 이 되어야 하지만, 20개씩 보여주는

경우에는 endPage 는 5가 되어야 합니다.

 

이를 이용해서 이전에 구한 endPage 값과 계산된 결과를 비교해서, 계산된 결과가 작은 경우에는 실제 endPage 는 최종 계산된

결과가 돼어만 합니다.

        
        int tempEndPage=(int)(Math.ceil(totalCount/(double)perPageNum));
        
        if(endPage >tempEndPage){
            endPage=tempEndPage;
        }

        

 

◆ prev next 의 계산

prev 의 경우는 startPage 가 1 이 아닌지를 검사하는 것으로 충분하다. 삼항연산자를 이용해서 1 이 면 false, 아닌 경우에는 true 값을 

가지도록 할 수 있다.

           prev startPage ==1 ? false :true;

 

next 의 경우는 뒤에 더 데이터가 남아 있는지에 대한 처리이므로, endPage * PerPageNum 이 totalCount 보다 작은지를 확인해

줘야 한다.

예를 들어 perPageNum 이 10 이고, endPage 가 10이 상황에서 totalCount 가 101 이라면 next 는 true 가 되어야 합니다.

      next =endPage *perPageNum >=totalCount ? false :true;

 

 

소스 

package config.paging;
//MySQL PageMaker
public class PageMaker {
	//mysql 들어갈 값은 page 가 아니라 pageStart, perPageNum 이다.
	//limit #{pageStart}, #{perPageNum}
	private int page;
	private int perPageNum;
	private int pageStart;
	
	//하단 페이징  << 1 2 3 4 5 6 7 8 9 10 >>
	private int totalCount; //전체 개수
	private int startPage; // 시작 페이지
	private int endPage;   // 끝페이지
	private boolean prev;  // 이전 여부 
	private boolean next;  // 다음 여부
	
	private int displayPageNum=10;
	
	
	public PageMaker() {
		this.page=1;          //초기 페이지는 1 
		this.perPageNum=10;  //limit 10 개씩 보여준다.
	}
	
	
	public void setPage(int page) {
		//페이지 번호가 0이거나 0보다 작으면 1페이지로 한다.
		//
		if(page <=0){
			this.page=1;
			return;
		}
		this.page = page;
	}
		
	// MyBatis SQL 의 Mapper 에서 인식해서 가져가는 파라미터 값 메소드 #{perPageNum}
	public void setPerPageNum(int perPageNum) {
		//몇개 씩 보여줄것인가 이다. 최대 100개씩 보여 줄것으로 설정한다.
		//만약 0보다 작거나 100 보다 크면 10으로 초기화 시킨다.
		if(perPageNum <=0 || perPageNum >100){
			this.perPageNum=10;
			return;
		}
		this.perPageNum = perPageNum;
	}


	// MyBatis SQL 의 Mapper 에서 인식해서 가져가는 파라미터 값 메소드 #{pageStart}
	public int getPageStart() {
		//실질적으로 Mybatis 에서  파라미터로 인식해서  가져오는 것은 get 이다.
		// 따라서 getPageStart 에서 값을 설정한다.
		//시작 데이터 번호 = (페이지 번호 -1 ) * 페이지당 보여지는 개수
		this.pageStart=(this.page -1)*perPageNum;
		return this.pageStart;
	}

	
	//전체 페이지 설정과 동시에 하단에 뿌려질 페이지 계산하기 
	public void setTotalCount(int totalCount) {
		this.totalCount = totalCount;
		calcData();
	}
	
	
	private void calcData(){
		//현재 페이지 번호 / 하단 페이지번호 수
		endPage=(int)(Math.ceil(page / (double)displayPageNum)*displayPageNum);
		
		startPage=(endPage - displayPageNum) +1;
		
		int tempEndPage=(int)(Math.ceil(totalCount/(double)perPageNum));
		
		if(endPage >tempEndPage){
			endPage=tempEndPage;
		}
		
		prev =startPage ==1 ? false :true;
		next =endPage *perPageNum >=totalCount ? false :true;
	}
~  
setter(), getter()

~~

}

 

테스트 하기

컨트롤러 

	
	//페이징 테스트
	@RequestMapping(value="/memberList", method=RequestMethod.GET)
	public String listMemberPage(PageMaker pageMaker, Model model) throws Exception{
		
		logger.info(pageMaker.toString());
		
		model.addAttribute("list", service.readListMember(pageMaker));
		//전체 페이지 개수 구한후 하단 페이징 처리 하기
		pageMaker.setTotalCount(1000);

		//페이지 메이커 attribute 로 저장  
		model.addAttribute("pageMaker", pageMaker);
		
		return "test/memberList";
	}
	

 

dao

	// 회원 목록 출력
	@Override
	public List<MemberVO> readListMember(PageMaker pageMaker) {
		return sqlSession.selectList(namespace + "readListMember", pageMaker);
	}

 

mapper

	<!-- 	회원 목록 출력 -->
	<select id="readListMember" resultType="net.macaronics.web.domain.MemberVO">		
		<![CDATA[
			
		   select mid, userid, userpw, username, email, regdate, updatedate, upoint 
		   
		    from TBL_MEMBER where mid >=0 order by mid desc 
		    
		    limit #{pageStart}, #{perPageNum}
		
		
		]]>		 
	</select>
	

 

View

memberList.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<!-- 합쳐지고 최소화된 최신 CSS -->
<link rel="stylesheet"
	href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">

<!-- 부가적인 테마 -->
<link rel="stylesheet"
	href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap-theme.min.css">

<!-- 합쳐지고 최소화된 최신 자바스크립트 -->
<script
	src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
</head>
<body>

	<div class="row">
		<div class="col-xs-12 col-sm-12">
		   <div class="table-responsive">
			<table class="table">
			<caption class="text-center"><h3>회원 리스트</h3></caption>
				<tr>
					<th>번호</th>
					<th>아이디</th>
					<th>비밀번호</th>
					<th>이름</th>
					<th>이메일</th>
					<th>점수</th>
					<th>등록일</th>
					<th>업데이트일</th>
				</tr>
				<c:forEach items="${list}" var="member">
					<tr>
						<th>${member.mid }</th>
						<th>${member.userid }</th>
						<th>${member.userpw }</th>
						<th>${member.username }</th>
						<th>${member.email }</th>
						<th>${member.upoint }</th>
						<th>${member.regdate }</th>
						<th>${member.updatedate }</th>
					</tr>
				</c:forEach>
				<tfoot>
					<tr>
					  <td colspan="8" class="text-center">
					  	  
					  	  <ul class="pagination">
					  	  	 <c:if test="${pageMaker.prev}">
					  	  	 	<li><a href="memberList?page=${pageMaker.startPage -1}">&laquo;</a></li>
					  	  	 </c:if>
					  	  
					  	  	 <c:forEach begin="${pageMaker.startPage }" end="${pageMaker.endPage}" var="idx">
					  	  	 	 <li
					  	  	 	   <c:out value="${pageMaker.page ==idx? 'class=active' : ''}" />
					  	  	 	  >
					  	  	 	   <a href="memberList?page=${idx}">${idx}</a></li>
					  	  	 </c:forEach>	
					  	  	 
					  	  	 <c:if test="${pageMaker.next && pageMaker.endPage >0 }">
					  	  	    <li><a href="memberList?page=${pageMaker.endPage +1}">&raquo;</a></li>
					  	  	 </c:if>
					  	  
					  	  </ul>
					  </td>
					</tr>
				</tfoot>
			</table>
			</div>
		</div>
	</div>


</body>
</html>

 

 

출력 화면

 

  3 단계   

스프링 MVC 의 UriComponectsBuilder를 이용하는 방식

 

                    ?가    자동으로 붙여서 출력 된다.

1.

	@Test
	public void testURI() throws Exception{
		UriComponents uriComponents=
				UriComponentsBuilder.newInstance()
				.path("/board/read")
				.queryParam("bno", 12)
				.queryParam("perPageNum", 20)
				.build();
		log.info("/board/read?bno=12&perPageNum=20");
		log.info(uriComponents.toString());
	}
	

 

INFO : net.macaronics.web.persistence.MemberDAOImplTest - /board/read?bno=12&perPageNum=20
INFO : net.macaronics.web.persistence.MemberDAOImplTest - /board/read?bno=12&perPageNum=20

 

2.

	@Test
	public void testURI2() throws Exception{
		
		UriComponents uriComponents=
				UriComponentsBuilder.newInstance()
				.path("/{module}/{page}")
				.queryParam("bno", 12)
				.queryParam("perPageNum", 20)
				.build()
				.expand("board", "read")
				.encode();
		log.info("/board/read?bno=12&perPageNum=20");
		log.info(uriComponents.toString());
	}
	

 

위와 같은 경우 미리 필요한 경로를 지정해 두고  '{module} 와 같은 경로를  board 로 {page} 를  read로 변경할 수 있다.

1번 방식이 더 쉬운것 같다.

 

페이지 메이커에 다음을 추가한다.

	public String makeQuery(int page){
		UriComponents uriComponents=
				UriComponentsBuilder.newInstance()
				.queryParam("page", page)
				.queryParam("perPageNum", perPageNum)
				.build();
		return uriComponents.toUriString();
	}
	

 

조회 페이지 경우

<a href="/board/readPage${pageMaker.makeQuery(pageMaker.page)}&bno=${boardVO.bno}'>

   22번 상세보기 페이지로  이동

</a>

 

변경

      <ul class="pagination">
           <c:if test="${pageMaker.prev}">
               <li><a href="memberList${pageMaker.makeQuery(pageMaker.startPage -1)}">&laquo;</a></li>
           </c:if>
      
           <c:forEach begin="${pageMaker.startPage }" end="${pageMaker.endPage}" var="idx">
                <li
                  <c:out value="${pageMaker.page ==idx? 'class=active' : ''}" />
                 >
                  <a href="memberList${pageMaker.makeQuery(idx)}">${idx}</a></li>
           </c:forEach>    
           
           <c:if test="${pageMaker.next && pageMaker.endPage >0 }">
              <li><a href="memberList${pageMaker.makeQuery(pageMaker.endPage +1)}">&raquo;</a></li>
           </c:if>
      
      </ul>

 

 

부트스트랩 전용  페이징 처리 만들기

페이지 메이커에 추가

public String bootStrapPagingHTML(String url){
	   StringBuffer sBuffer=new StringBuffer();
	   sBuffer.append("<ul class='pagination'>");
	   if(prev){
		   sBuffer.append("<li><a href='"+url+makeQuery(1)+"'>처음</a></li>");
	   }
	   
	   if(prev){
		   sBuffer.append("<li><a href='"+url+makeQuery(startPage-1)+"'>&laquo;</a></li>");
	   }

	    String active="";
	   	for(int i=startPage; i <=endPage; i++){
	   		if(page==i){
	   			 active="class=active";
	   		}else{
	   			active="";
	   		}
	   		sBuffer.append("<li " +active+" >");
	   		sBuffer.append("<a href='"+url+makeQuery(i)+"'>"+i+"</a></li>");
	   		sBuffer.append("</li>");
	   	}
	   	
		if(next && endPage>0){
			sBuffer.append("<li><a href='"+url+makeQuery(endPage+1)+"'>&raquo;</a></li>");			
		}
		
		if(next && endPage>0){
			sBuffer.append("<li><a href='"+url+makeQuery(tempEndPage)+"'>마지막</a></li>");			
		}		
		
		sBuffer.append("</ul>");	
		return sBuffer.toString();
	}

 

 

view

다음과 같이 url 에만 넣으면 이미지 처럼 출력 된다.

              

          <br>
                
                ${pageMaker.bootStrapPagingHTML("memberList") }
                
                 </td>

 

http://localhost:7070/memberList?page=90&perPageNum=10

 

  3단계까지 검색 제외 페이징 처리 최종 산출물    

 

PageMaker

package config.paging;

import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;

//MySQL PageMaker
public class PageMaker {
	//mysql 들어갈 값은 page 가 아니라 pageStart, perPageNum 이다.
	//limit #{pageStart}, #{perPageNum}
	private int page;
	private int perPageNum;
	private int pageStart;
	
	//하단 페이징  << 1 2 3 4 5 6 7 8 9 10 >>
	private int totalCount; //전체 개수
	private int startPage; // 시작 페이지
	private int endPage;   // 끝페이지
	private boolean prev;  // 이전 여부 
	private boolean next;  // 다음 여부
	
	private int displayPageNum=10;
	
	private int tempEndPage;
	
	public PageMaker() {
		this.page=1;          //초기 페이지는 1 
		this.perPageNum=10;  //limit 10 개씩 보여준다.
	}
	
	
	public void setPage(int page) {
		//페이지 번호가 0이거나 0보다 작으면 1페이지로 한다.
		//
		if(page <=0){
			this.page=1;
			return;
		}
		this.page = page;
	}
		
	// MyBatis SQL 의 Mapper 에서 인식해서 가져가는 파라미터 값 메소드 #{perPageNum}
	public void setPerPageNum(int perPageNum) {
		//몇개 씩 보여줄것인가 이다. 최대 100개씩 보여 줄것으로 설정한다.
		//만약 0보다 작거나 100 보다 크면 10으로 초기화 시킨다.
		if(perPageNum <=0 || perPageNum >100){
			this.perPageNum=10;
			return;
		}
		this.perPageNum = perPageNum;
	}


	// MyBatis SQL 의 Mapper 에서 인식해서 가져가는 파라미터 값 메소드 #{pageStart}
	public int getPageStart() {
		//실질적으로 Mybatis 에서  파라미터로 인식해서  가져오는 것은 get 이다.
		// 따라서 getPageStart 에서 값을 설정한다.
		//시작 데이터 번호 = (페이지 번호 -1 ) * 페이지당 보여지는 개수
		this.pageStart=(this.page -1)*perPageNum;
		return this.pageStart;
	}

	
	//전체 페이지 설정과 동시에 하단에 뿌려질 페이지 계산하기 
	public void setTotalCount(int totalCount) {
		this.totalCount = totalCount;
		calcData();
	}
	
	
	private void calcData(){
		//현재 페이지 번호 / 하단 페이지번호 수
		endPage=(int)(Math.ceil(page / (double)displayPageNum)*displayPageNum);
		
		startPage=(endPage - displayPageNum) +1;
		
		tempEndPage=(int)(Math.ceil(totalCount/(double)perPageNum));
		
		if(endPage >tempEndPage){
			endPage=tempEndPage;
		}
		
		prev =startPage ==1 ? false :true;
		next =endPage *perPageNum >=totalCount ? false :true;
	}
	
	
	
	public String makeQuery(int page){
		UriComponents uriComponents=
				UriComponentsBuilder.newInstance()
				.queryParam("page", page)
				.queryParam("perPageNum", perPageNum)
				.build();
		return uriComponents.toUriString();
	}
	
	
	public String bootStrapPagingHTML(String url){
	   StringBuffer sBuffer=new StringBuffer();
	   sBuffer.append("<ul class='pagination'>");
	   if(prev){
		   sBuffer.append("<li><a href='"+url+makeQuery(1)+"'>처음</a></li>");
	   }
	   
	   if(prev){
		   sBuffer.append("<li><a href='"+url+makeQuery(startPage-1)+"'>&laquo;</a></li>");
	   }

	    String active="";
	   	for(int i=startPage; i <=endPage; i++){
	   		if(page==i){
	   			 active="class=active";
	   		}else{
	   			active="";
	   		}
	   		sBuffer.append("<li " +active+" >");
	   		sBuffer.append("<a href='"+url+makeQuery(i)+"'>"+i+"</a></li>");
	   		sBuffer.append("</li>");
	   	}
	   	
		if(next && endPage>0){
			sBuffer.append("<li><a href='"+url+makeQuery(endPage+1)+"'>&raquo;</a></li>");			
		}
		
		if(next && endPage>0){
			sBuffer.append("<li><a href='"+url+makeQuery(tempEndPage)+"'>마지막</a></li>");			
		}		
		
		sBuffer.append("</ul>");	
		return sBuffer.toString();
	}
	
		
	
	
	
	public int getPage() {
		return page;
	}



	public int getPerPageNum() {
		return perPageNum;
	}


	public void setPageStart(int pageStart) {
		this.pageStart = pageStart;
	}


	public int getTotalCount() {
		return totalCount;
	}


	public int getStartPage() {
		return startPage;
	}


	public void setStartPage(int startPage) {
		this.startPage = startPage;
	}


	public int getEndPage() {
		return endPage;
	}


	public void setEndPage(int endPage) {
		this.endPage = endPage;
	}


	public boolean isPrev() {
		return prev;
	}


	public void setPrev(boolean prev) {
		this.prev = prev;
	}


	public boolean isNext() {
		return next;
	}


	public void setNext(boolean next) {
		this.next = next;
	}


	public int getDisplayPageNum() {
		return displayPageNum;
	}


	public void setDisplayPageNum(int displayPageNum) {
		this.displayPageNum = displayPageNum;
	}


	@Override
	public String toString() {
		return "PageMaker [page=" + page + ", perPageNum=" + perPageNum + ", pageStart=" + pageStart + ", totalCount="
				+ totalCount + ", startPage=" + startPage + ", endPage=" + endPage + ", prev=" + prev + ", next=" + next
				+ ", displayPageNum=" + displayPageNum + "]";
	}
	
	
	
	
}

 

Controller

	
	
	//페이징 테스트
	@RequestMapping(value="/memberList", method=RequestMethod.GET)
	public String listMemberPage(PageMaker pageMaker, Model model) throws Exception{	
		
	        //전체 페이지 개수 구한후 하단 페이징 처리 하기
                pageMaker.setTotalCount(1000);
               //페이지 메이커 attribute 로 저장  
                model.addAttribute("pageMaker", pageMaker);
               model.addAttribute("list", service.readListMember(pageMaker));
        
		
		
		return "test/memberList";
	}

 

View

					<!-- jsp 로 출력 할경우  -->  	  
 	  <ul class="pagination">
 	  	 <c:if test="${pageMaker.prev}">
 	  	 	<li><a href="memberList${pageMaker.makeQuery(pageMaker.startPage -1)}">&laquo;</a></li>
 	  	 </c:if>
 	  
 	  	 <c:forEach begin="${pageMaker.startPage }" end="${pageMaker.endPage}" var="idx">
 	  	 	 <li
 	  	 	   <c:out value="${pageMaker.page ==idx? 'class=active' : ''}" />
 	  	 	  >
 	  	 	   <a href="memberList${pageMaker.makeQuery(idx)}">${idx}</a></li>
 	  	 </c:forEach>	
 	  	 
 	  	 <c:if test="${pageMaker.next && pageMaker.endPage >0 }">
 	  	    <li><a href="memberList${pageMaker.makeQuery(pageMaker.endPage +1)}">&raquo;</a></li>
 	  	 </c:if>
 	  
 	  </ul>
				
				<br>
				<!-- bootStrapPagingHTML 로 출력 할경우  -->  
				${pageMaker.bootStrapPagingHTML("memberList") }
				
				 </td>

 

 

 

 

  4단계 - 페이징 처리에 검색처리 추가 하기   

 

PageMaker  => PageMakerAndSearch  

변경

그리고 다음 메소드를 추가한다. 

컨트롤 부분은 변경사항이 없다.  왜냐하면은 PageMakerAndSearch  의 하나의 클래스에  일치 시켰기 때문에  검색처리에서 

두 메소드만 추가하면 크게 변경할 사항이없다. 

Mybatis 에서 Query 처리와  화면 View 에서 검색 폼을 만들기 만 하면 된다.

	
	//검색 추가 페이지 파라미터 
	public String makeSearch(int page){
		UriComponents uriComponents=
				UriComponentsBuilder.newInstance()
				.queryParam("page", page)
				.queryParam("perPageNum", perPageNum)
				.queryParam("searchType", searchType)
				.queryParam("keyword", keyword)
				.build();
		return uriComponents.toUriString();
	}
	
	
	
	//검색 추가 페이징 부트스트랩 출력
	public String bootStrapPagingSearchHTML(String url){
	   StringBuffer sBuffer=new StringBuffer();
	   sBuffer.append("<ul class='pagination'>");
	   if(prev){
		   sBuffer.append("<li><a href='"+url+makeSearch(1)+"'>처음</a></li>");
	   }
	   
	   if(prev){
		   sBuffer.append("<li><a href='"+url+makeSearch(startPage-1)+"'>&laquo;</a></li>");
	   }

	    String active="";
	   	for(int i=startPage; i <=endPage; i++){
	   		if(page==i){
	   			 active="class=active";
	   		}else{
	   			active="";
	   		}
	   		sBuffer.append("<li " +active+" >");
	   		sBuffer.append("<a href='"+url+makeSearch(i)+"'>"+i+"</a></li>");
	   		sBuffer.append("</li>");
	   	}
	   	
		if(next && endPage>0){
			sBuffer.append("<li><a href='"+url+makeSearch(endPage+1)+"'>&raquo;</a></li>");			
		}
		
		if(next && endPage>0){
			sBuffer.append("<li><a href='"+url+makeSearch(tempEndPage)+"'>마지막</a></li>");			
		}		
		
		sBuffer.append("</ul>");	
		return sBuffer.toString();
	}
	
	

 

 

controller 

pageMaker 에 전체 카운터 만 넣은 코드만 작성하면된다.

    //페이징 and Search 
    @RequestMapping(value="/memberListSearch", method=RequestMethod.GET)
    public String listPageSearch(@ModelAttribute("pageMaker") PageMakerAndSearch pageMaker, Model model) throws Exception{
        
        //전체 페이지 개수 구한후 하단 페이징 처리 하기
        pageMaker.setTotalCount(service.listPageCount(pageMaker));
        //페이지 메이커 attribute 로 저장  
        model.addAttribute("pageMaker", pageMaker);    
        model.addAttribute("list", service.listPageSearch(pageMaker));
        
        return "test/memberList";
    }

 

view

<div class="row">
       <div class="col-xs-3 col-sm-3"> </div>
        <div class="col-xs-8 col-sm-8">
          <div class="text-right">
               <div class="box-body">
               
                <div class="col-sm-4">
                   <select name="searchType" class="form-control" id="searchType">
                     <option value="n" <c:out value="${pageMaker.searchType == null ? 'selected' : '' }"/>  >----</option>
                     <option value="userid" <c:out value="${pageMaker.searchType eq 'userid' ? 'selected' : '' }"/> >아이디</option>
                     <option value="username"  <c:out value="${pageMaker.searchType eq 'username' ? 'selected' : '' }"/> >이름</option>
                     <option value="email" <c:out value="${pageMaker.searchType eq 'email' ? 'selected' : '' }"/>  >이메일</option>
                     <option value="all" <c:out value="${pageMaker.searchType eq 'all' ? 'selected' : '' }"/>  >아이디 or 이름 or 이메일</option>
                   </select>
               </div>
               
               <div class="col-sm-4">
                  <input type="text" name="keyword" id="keywordInput"  class="form-control" value="${pageMaker.keyword}">     
               </div>
               <div class="col-sm-4 text-left">
                  <button id="searchBtn" class="btn btn-success" >검색</button>
               </div>
                   
               </div>
          </div>
        </div>
     </div>

    




			
									<!-- jsp 로 출력 할경우  -->  	  
 	  <ul class="pagination">
 	  	 <c:if test="${pageMaker.prev}">
 	  	 	<li><a href="memberListSearch${pageMaker.makeSearch(pageMaker.startPage -1)}">&laquo;</a></li>
 	  	 </c:if>
 	  
 	  	 <c:forEach begin="${pageMaker.startPage }" end="${pageMaker.endPage}" var="idx">
 	  	 	 <li
 	  	 	   <c:out value="${pageMaker.page ==idx? 'class=active' : ''}" />
 	  	 	  >
 	  	 	   <a href="memberListSearch${pageMaker.makeSearch(idx)}">${idx}</a></li>
 	  	 </c:forEach>	
 	  	 
 	  	 <c:if test="${pageMaker.next && pageMaker.endPage >0 }">
 	  	    <li><a href="memberListSearch${pageMaker.makeSearch(pageMaker.endPage +1)}">&raquo;</a></li>
 	  	 </c:if> 
 	  </ul>
				
				<br>
				<!-- bootStrapPagingSearchHTML 로 출력 할경우  -->  
				${pageMaker.bootStrapPagingSearchHTML("memberListSearch") }
				
				 </td>
					


 

자바스크립트 주의 할 사항은 makeQuery 로 해야 한다.   makeSearch로 하면 파라미터가 중복되어서 안 된다.

<script>
$(document).ready(function(){
	
	$("#searchBtn").on("click",
		function(event){
		
		  var url ="memberListSearch${pageMaker.makeQuery(1)}";
		  url +="&searchType="+searchType()+"&keyword="+keywordInput();
	      self.location =url;	
	});

});

function searchType(){
	return $("#searchType").val();
}

function keywordInput(){
	return $("#keywordInput").val();
}
</script>

 

 

MyBatis 동적 SQL

 

자세한 내용은 MyBatis  사이트에 있다.

 

http://www.mybatis.org/mybatis-3/ko/dynamic-sql.html

마이바티스의 가장 강력한 기능 중 하나는 동적 SQL을 처리하는 방법이다. JDBC나 다른 유사한 프레임워크를 사용해본 경험이 있다면 동적으로 SQL 을 구성하는 것이 얼마나 힘든 작업인지 이해할 것이다. 간혹 공백이나 콤마를 붙이는 것을 잊어본 적도 있을 것이다. 동적 SQL 은 그만큼 어려운 것이다.

동적 SQL 을 사용하는 것은 결코 파티가 될 수 없을 것이다. 마이바티스는 강력한 동적 SQL 언어로 이 상황은 개선한다.

동적 SQL 엘리먼트들은 JSTL이나 XML기반의 텍스트 프로세서를 사용해 본 사람에게는 친숙할 것이다. 마이바티스의 이전 버전에서는 알고 이해해야 할 엘리먼트가 많았다. 마이바티스 3 에서는 이를 크게 개선했고 실제 사용해야 할 엘리먼트가 반 이하로 줄었다. 마이바티스의 다른 엘리먼트의 사용을 최대한 제거하기 위해 OGNL 기반의 표현식을 가져왔다.

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

 

<sql id="search">
    <if test="searchType !=null">
            <if test="searchType == 'userid'.toString()">
                and userid like CONCAT('%', #{keyword}, '%')
            </if>
    
            <if test="searchType == 'username'.toString()">
                and username like CONCAT('%', #{keyword}, '%')
            </if>
    
            <if test="searchType == 'email'.toString()">
                and email like CONCAT('%', #{keyword}, '%')
            </if>
            
            <if test="searchType == 'all'.toString()">
                and ( 
                     userid like CONCAT('%', #{keyword}, '%')
                    OR
                     username like CONCAT('%', #{keyword}, '%')
                    OR
                     email like CONCAT('%', #{keyword}, '%')
                )
            </if>
            
    </if>
</sql>


    <!-- 회원 목록 출력 페이징 처리 및 검색처리 -->
    <select id="listPageSearch" resultType="net.macaronics.web.domain.MemberVO">        
        <![CDATA[    
            select * from TBL_MEMBER where mid >0 
        ]]>
        
          <include refid="search" ></include>
            
        <![CDATA[    
            order by mid desc             
            limit #{pageStart}, #{perPageNum}
        ]]>
    </select>


    <select id="listPageCount" resultType="int">        
        <![CDATA[
            select count(mid) from TBL_MEMBER where mid >0 
        ]]>
        
         <include refid="search" ></include>
    </select>

 

 

count(*)  처리하면 에러 난다.

 

 

url 이 다음과 같이 출력되는 지 확인한다.

http://localhost:7070/memberListSearch?page=3&perPageNum=10&searchType=&keyword=

 

http://localhost:7070/memberListSearch?page=1&perPageNum=10&searchType=name&keyword=%EC%B5%9C%EC%A4%80%ED%98%B8

 

 

 

  페이징 처리 및 검색처리 완료 소스     

 

PageMakerAndSearch

package config.paging;

import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;

//MySQL PageMaker
public class PageMakerAndSearch {
	//mysql 들어갈 값은 page 가 아니라 pageStart, perPageNum 이다.
	//limit #{pageStart}, #{perPageNum}
	private int page;
	private int perPageNum;
	private int pageStart;
	
	//하단 페이징  << 1 2 3 4 5 6 7 8 9 10 >>
	private int totalCount; //전체 개수
	private int startPage; // 시작 페이지
	private int endPage;   // 끝페이지
	private boolean prev;  // 이전 여부 
	private boolean next;  // 다음 여부
	
	private int displayPageNum=10;
	
	private int tempEndPage; //마지막 페이지
	
	//검색처리 추가
	private String searchType;
	private String keyword;
	
	
	public PageMakerAndSearch() {
		this.page=1;          //초기 페이지는 1 
		this.perPageNum=10;  //limit 10 개씩 보여준다.
	}
	
	
	public void setPage(int page) {
		//페이지 번호가 0이거나 0보다 작으면 1페이지로 한다.
		//
		if(page <=0){
			this.page=1;
			return;
		}
		this.page = page;
	}
		
	// MyBatis SQL 의 Mapper 에서 인식해서 가져가는 파라미터 값 메소드 #{perPageNum}
	public void setPerPageNum(int perPageNum) {
		//몇개 씩 보여줄것인가 이다. 최대 100개씩 보여 줄것으로 설정한다.
		//만약 0보다 작거나 100 보다 크면 10으로 초기화 시킨다.
		if(perPageNum <=0 || perPageNum >100){
			this.perPageNum=10;
			return;
		}
		this.perPageNum = perPageNum;
	}


	// MyBatis SQL 의 Mapper 에서 인식해서 가져가는 파라미터 값 메소드 #{pageStart}
	public int getPageStart() {
		//실질적으로 Mybatis 에서  파라미터로 인식해서  가져오는 것은 get 이다.
		// 따라서 getPageStart 에서 값을 설정한다.
		//시작 데이터 번호 = (페이지 번호 -1 ) * 페이지당 보여지는 개수
		this.pageStart=(this.page -1)*perPageNum;
		return this.pageStart;
	}

	
	//전체 페이지 설정과 동시에 하단에 뿌려질 페이지 계산하기 
	public void setTotalCount(int totalCount) {
		this.totalCount = totalCount;
		calcData();
	}
	
	
	private void calcData(){
		//현재 페이지 번호 / 하단 페이지번호 수
		endPage=(int)(Math.ceil(page / (double)displayPageNum)*displayPageNum);
		
		startPage=(endPage - displayPageNum) +1;
		
		tempEndPage=(int)(Math.ceil(totalCount/(double)perPageNum));
		
		if(endPage >tempEndPage){
			endPage=tempEndPage;
		}
		
		prev =startPage ==1 ? false :true;
		next =endPage *perPageNum >=totalCount ? false :true;
	}
	
	
	//일반적인 페이징 처리 파라미터 출력 데이터 ex) /memberList?page=4&perPageNum=10
	public String makeQuery(int page){
		UriComponents uriComponents=
				UriComponentsBuilder.newInstance()
				.queryParam("page", page)
				.queryParam("perPageNum", perPageNum)
				.build();
		return uriComponents.toUriString();
	}
	
	//일반적인 페이징 부트스트랩 출력
	public String bootStrapPagingHTML(String url){
	   StringBuffer sBuffer=new StringBuffer();
	   sBuffer.append("<ul class='pagination'>");
	   if(prev){
		   sBuffer.append("<li><a href='"+url+makeQuery(1)+"'>처음</a></li>");
	   }
	   
	   if(prev){
		   sBuffer.append("<li><a href='"+url+makeQuery(startPage-1)+"'>&laquo;</a></li>");
	   }

	    String active="";
	   	for(int i=startPage; i <=endPage; i++){
	   		if(page==i){
	   			 active="class=active";
	   		}else{
	   			active="";
	   		}
	   		sBuffer.append("<li " +active+" >");
	   		sBuffer.append("<a href='"+url+makeQuery(i)+"'>"+i+"</a></li>");
	   		sBuffer.append("</li>");
	   	}
	   	
		if(next && endPage>0){
			sBuffer.append("<li><a href='"+url+makeQuery(endPage+1)+"'>&raquo;</a></li>");			
		}
		
		if(next && endPage>0){
			sBuffer.append("<li><a href='"+url+makeQuery(tempEndPage)+"'>마지막</a></li>");			
		}		
		
		sBuffer.append("</ul>");	
		return sBuffer.toString();
	}
	
	
	//검색 추가 페이지 파라미터 
	public String makeSearch(int page){
		UriComponents uriComponents=
				UriComponentsBuilder.newInstance()
				.queryParam("page", page)
				.queryParam("perPageNum", perPageNum)
				.queryParam("searchType", searchType)
				.queryParam("keyword", keyword)
				.build();
		return uriComponents.toUriString();
	}
	
	
	
	//검색 추가 페이징 부트스트랩 출력
	public String bootStrapPagingSearchHTML(String url){
	   StringBuffer sBuffer=new StringBuffer();
	   sBuffer.append("<ul class='pagination'>");
	   if(prev){
		   sBuffer.append("<li><a href='"+url+makeSearch(1)+"'>처음</a></li>");
	   }
	   
	   if(prev){
		   sBuffer.append("<li><a href='"+url+makeSearch(startPage-1)+"'>&laquo;</a></li>");
	   }

	    String active="";
	   	for(int i=startPage; i <=endPage; i++){
	   		if(page==i){
	   			 active="class=active";
	   		}else{
	   			active="";
	   		}
	   		sBuffer.append("<li " +active+" >");
	   		sBuffer.append("<a href='"+url+makeSearch(i)+"'>"+i+"</a></li>");
	   		sBuffer.append("</li>");
	   	}
	   	
		if(next && endPage>0){
			sBuffer.append("<li><a href='"+url+makeSearch(endPage+1)+"'>&raquo;</a></li>");			
		}
		
		if(next && endPage>0){
			sBuffer.append("<li><a href='"+url+makeSearch(tempEndPage)+"'>마지막</a></li>");			
		}		
		
		sBuffer.append("</ul>");	
		return sBuffer.toString();
	}
	
	
	
	
	public int getPage() {
		return page;
	}



	public int getPerPageNum() {
		return perPageNum;
	}


	public void setPageStart(int pageStart) {
		this.pageStart = pageStart;
	}


	public int getTotalCount() {
		return totalCount;
	}


	public int getStartPage() {
		return startPage;
	}


	public void setStartPage(int startPage) {
		this.startPage = startPage;
	}


	public int getEndPage() {
		return endPage;
	}


	public void setEndPage(int endPage) {
		this.endPage = endPage;
	}


	public boolean isPrev() {
		return prev;
	}


	public void setPrev(boolean prev) {
		this.prev = prev;
	}


	public boolean isNext() {
		return next;
	}


	public void setNext(boolean next) {
		this.next = next;
	}


	public int getDisplayPageNum() {
		return displayPageNum;
	}


	public void setDisplayPageNum(int displayPageNum) {
		this.displayPageNum = displayPageNum;
	}


	public String getSearchType() {
		return searchType;
	}


	public void setSearchType(String searchType) {
		this.searchType = searchType;
	}


	public String getKeyword() {
		return keyword;
	}


	public void setKeyword(String keyword) {
		this.keyword = keyword;
	}

	

	@Override
	public String toString() {
		return "PageMakerAndSearch [page=" + page + ", perPageNum=" + perPageNum + ", pageStart=" + pageStart
				+ ", totalCount=" + totalCount + ", startPage=" + startPage + ", endPage=" + endPage + ", prev=" + prev
				+ ", next=" + next + ", displayPageNum=" + displayPageNum + ", tempEndPage=" + tempEndPage
				+ ", searchType=" + searchType + ", keyword=" + keyword + "]";
	}


	
	
	
	
	
	
	
}

 

 

 

 

 


 

 

 

 

 

 

제작 : macaronics.net - Developer  Jun Ho Choi

소스 :  소스가 필요한 분은 비밀 댓글로 작성해 주세요.

 

 

 

 

about author

PHRASE

Level 60  라이트

착한 사람에게는 선행을 하기 쉽고, 악한 사람에게는 선행을 하기 어렵다. 악한 사람에게는 악행을 하기 쉽고, 착한 사람에게는 악행을 하기 힘들다. -자설경

댓글 ( 6)

댓글 남기기

작성