[Spring boot] mysql + mybatis 페이징 처리 방법

페이징 플러그인을 적용해 간단히... 또 검색 기능까지 활용할 수 있지만..

 

어디를 면접가도

 

페이징 할 수 있어요?

네!

인터넷으로 검색 안 하고요?

그건... 좀.. 오래 걸릴 거 같아요..

라는 뻔한 레퍼토리가 나오기 십상이다.

 

이 글을 통해 다시 한번 기억을 되짚어 보면서 반성해보려 한다.

이 글은 유튜브 동영상에서 조금 모자랐던 코드를 수정해서 만들었다.

 

-결과물-

 

생성할 페이징 기능(목표):

1. 보여줄 글 개수 정하는 셀렉트 박스 -> 10개, 20개, 30개

2. 페이징(페이지는 5개씩)

3. 이전, 다음 버튼 생성

 

 

출처:https://www.youtube.com/watch?v=aLBemo4oo60&t=1449s

 

출처:https://www.youtube.com/watch?v=aLBemo4oo60&t=1449s

 

참고하면서 만들면 편할거 같다.

페이징을 하기위해 필요한 파일작성을 차근차근 프로젝트 상단부터 아래로 만들어 보자.

 

프로젝트에서 생성 및 수정해야할 곳

MainContoller.java

@Controller
public class MainController {
	@Resource(name="ServiceMember")
	private memberImpl aa;

	@RequestMapping(value="/")
	public String main(Model model) {
		
		
		return "index";
	}
	@RequestMapping(value="tables")
	public String tables(Model model,@RequestParam(defaultValue = "1") String pagenum,@RequestParam(defaultValue = "10") String contentnum) throws Exception{
		aa.execute(model, pagenum, contentnum);
		return "tables";
	}
}

@RequestParam(defaultValue ="값")은

맵핑으로 tables로 들어왔을 때 변수 pagenum과 contentnum의 값을 같이 보내주지 않은 경우 default값으로 원하는 값을 설정해줄 수 있다.

 

 

MainMapper.java

@Repository("daoDB")
public interface MainMapper {
	
	public List<MemberDTO> testlist(int pagenum, int contentnum);
	public int testcount();
	
}

 

BoardPager.java

public class BoardPager  {

    private int totalcount; // 페이징에 적용할 전체 데이터 갯수
    private int pagenum; // 현재 페이지 번호
    private int contentnum; // 한페이지에 몇개 표시할지
    private int startPage=1; // 현재 페이지 블록의 시작 페이지
    private int endPage=5; // 현재 페이지 블럭의 마지막 페이지
    private boolean prev; // 이전 페이지로 가는 화살표
    private boolean next; //  다음 페이지로 가는 화살표
    private int currentblock; // 현재 페이지 블록
    private int lastblock; //  마지막 페이지 블록

    public void prevnext(int pagenum){
        //이전 , 다음 페이지 블록

        if(calcpage(totalcount,contentnum)<6){
            setPrev(false);
            setNext(false);
       }else if(pagenum>0 && pagenum<6){
           setPrev(false);
           setNext(true);
       }else if(getLastblock() == getCurrentblock()){
            setPrev(true);
            setNext(false);
        }else{
            setPrev(true);
            setNext(true);
        }
    }
    public int calcpage(int totalcount, int contentnum){ // 전체페이지 수를 계산하는 함수
        //125 / 10 => 12.5
        //13페이지
        int totalpage = totalcount / contentnum;
        if(totalcount%contentnum>0){
            totalpage++;
        }
        return totalpage;
    }

    public int getTotalcount() {
        return totalcount;
    }

    public void setTotalcount(int totalcount) {
        this.totalcount = totalcount;
    }

    public int getPagenum() {
        return pagenum;
    }

    public void setPagenum(int pagenum) {
        this.pagenum = pagenum;
    }

    public int getContentnum() {
        return contentnum;
    }

    public void setContentnum(int contentnum) {
        this.contentnum = contentnum;
    }

    public int getStartPage() {
        return startPage;
    }

    public void setStartPage(int currentblock) {
        this.startPage = (currentblock*5)-4;
        /*
        *  1  // 1 2 3 4 5
        *  2  // 6 7 8 9 10
        *  3  // 11 12 13
        * */
     }

    public int getEndPage() {
        return endPage;
    }

    public void setEndPage(int getlastblock, int getcurrentblock) {
        // 마지막 페이지 블록을 구하는 곳
        if(getlastblock == getcurrentblock){
            this.endPage = calcpage(getTotalcount(),getContentnum()); // 전체페이지 개수가 오는곳
        }else{
            this.endPage = getStartPage()+4;   
        }
    }

    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 getCurrentblock() {
        return currentblock;
    }

    public void setCurrentblock(int pagenum) {
        //현재페이지 블록 구하는 곳  페이지 번호를 통해서 구한다.
        //페이지 번호 / 페이지 그룹안의 페이지 개수
        // 1p => 1 / 5 => 0.2   + 1 = 현재 페이지블록 1
        // 3p => 3 / 5 => 0.xx  + 1 = 현재 페이지 블록 1
        this.currentblock = pagenum/5;
        if(pagenum%5>0){
            this.currentblock++;
        }
    }

    public int getLastblock() {
        return lastblock;
    }

    public void setLastblock(int lastblock) {
        //10 , 5  = > 10 * 5 => 50
        // 12 5 / 50
        // 3
        this.lastblock = totalcount / (5*this.contentnum);

        if(totalcount %(5*this.contentnum)>0){
            this.lastblock++;
        }
    }
}

 

MemberDTO.java

public class MemberDTO {
	private String id;
	private String password;
	
	
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	
	
}

 

MemberImpl.java

@Service("ServiceMember")
public class memberImpl implements ServiceMember{
	
	@Resource(name="daoDB")
	private MainMapper mm;
	
	@Override
	public int execute(Model model,String pagenum, String contentnum) {
		
		BoardPager pagemaker = new BoardPager();

        int cpagenum = Integer.parseInt(pagenum);
        int ccontentnum = Integer.parseInt(contentnum);

        List<MemberDTO> testList = null;

        pagemaker.setTotalcount(mm.testcount()); // mapper 전체 게시글 개수를 지정한다
        pagemaker.setPagenum(cpagenum-1);   // 현재 페이지를 페이지 객체에 지정한다 -1 을 해야 쿼리에서 사용할수 있다
        pagemaker.setContentnum(ccontentnum); // 한 페이지에 몇개씩 게시글을 보여줄지 지정한다.
        pagemaker.setCurrentblock(cpagenum); // 현재 페이지 블록이 몇번인지 현재 페이지 번호를 통해서 지정한다.
        pagemaker.setLastblock(pagemaker.getTotalcount()); // 마지막 블록 번호를 전체 게시글 수를 통해서 정한다.

        pagemaker.prevnext(cpagenum);//현재 페이지 번호로 화살표를 나타낼지 정한다.
        pagemaker.setStartPage(pagemaker.getCurrentblock()); // 시작 페이지를 페이지 블록번호로 정한다.
        pagemaker.setEndPage(pagemaker.getLastblock(),pagemaker.getCurrentblock());
        //마지막 페이지를 마지막 페이지 블록과 현재 페이지 블록 번호로 정한다.
        
        if(ccontentnum == 10){//선택 게시글 수
        	testList = mm.testlist(pagemaker.getPagenum()*10,pagemaker.getContentnum());
        }else if(ccontentnum == 20){
        	testList = mm.testlist(pagemaker.getPagenum()*20,pagemaker.getContentnum());
        }else if(ccontentnum ==30){
        	testList = mm.testlist(pagemaker.getPagenum()*30,pagemaker.getContentnum());
        }
        
		model.addAttribute("test",testList);
        model.addAttribute("page",pagemaker);
		
		return 0;
	}

}

 

ServiceMember.java

public interface ServiceMember {
	
	public int execute(Model model,String pagenum, String contentnum);
}

 

MainMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.example.demo.DAO.MainMapper">

<select id="testlist" resultType="com.example.demo.DTO.MemberDTO">

	select * from test order by id limit #{pagenum}, #{contentnum}
</select>
<select id="testcount" resultType="int">
	select count(*) from test
</select>

</mapper>

Table쪽은 id,password 칼럼으로 구성했고 id는 시퀀스를 생성해줬다.

(페이징처리가 잘되었는지 테스트해보기 위해 데이터가 많이 필요하기 때문에 해줬다.)

 

 

데이터 생성을 위해 해준 쿼리문

insert into testdemo.test(id,password) values(1,"0000");
//우선 데이터를 하나 생성해준다.
insert into testdemo.test(password) (select password from testdemo.test);
//values부분에 테이블 전체값을 넣어줌으로서 쿼리문을 한 번 실행 해줄 때 마다 테이블의 데이터가 곱하기2씩 늘어나게 된다. 

1...2...4...8...16...32 ...

 

tables.jsp

<script type="text/javascript">

/*한페이지당 게시물 */
function page(idx){
  var pagenum = idx;
  var contentnum = $("#contentnum option:selected").val();

  if(contentnum == 10){
    location.href="${pageContext.request.contextPath}/tables?pagenum="+pagenum+"&contentnum="+contentnum

  }else if(contentnum == 20){
    location.href="${pageContext.request.contextPath}/tables?pagenum="+pagenum+"&contentnum="+contentnum

  }else if(contentnum == 30){
    location.href="${pageContext.request.contextPath}/tables?pagenum="+pagenum+"&contentnum="+contentnum

  }
}

</script>
  <--! 한번에 보여줄 개수 정하기 -->
    <select name="contentnum" id="contentnum" onchange="page(1)">
      <option value="10" <c:if test="${page.getContentnum() == 10 }">selected="selected"</c:if> >10 개</option>
      <option value="20" <c:if test="${page.getContentnum() == 20 }">selected="selected"</c:if> >20 개</option>
      <option value="30" <c:if test="${page.getContentnum() == 30 }">selected="selected"</c:if> >30 개</option>
    </select>

    <--! 페이징 데이터-->
      <table class="table table-bordered" width="100%" cellspacing="0">
        <thead>
          <tr>
            <th>id</th>
            <th>password</th>
          </tr>
        </thead>
        <tbody>
          <c:forEach items="${test}" var="test">
            <tr>
              <td>${test.id}</td>
              <td>${test.password}</td>
            </tr>
          </c:forEach>
        </tbody>
        <tfoot>
          <div class="row">
            <tr>
              <td colspan="14">
                <--! 페이징 -->
                  <c:if test="${page.prev}">
                    <a href="javascript:page(${page.getStartPage()-1});">&laquo;</a>
                  </c:if>
                  <c:forEach begin="${page.getStartPage()}" end="${page.getEndPage()}" var="idx">
                    <a href="javascript:page(${idx});">${idx}</a>
                  </c:forEach>
                  <c:if test="${page.next}">
                    <a href="javascript:page(${page.getEndPage()+1});">&raquo;</a>
                  </c:if>
              </td>
            </tr>
          </div>
        </tfoot>
      </table>

본인 프로젝트에 적당히 넣어주도록 하자. 

필자는 sb-admin2 템플릿을 사용했기 때문에 태그에 붙어있는 class나 colspan등은 신경 쓰지 않아도 된다.

 

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

jsp에서 c태그를 사용하기 위해서는 위와 같은 설정이 필요하다.(jsp파일 맨 상단에 넣어주도록 하자.)

 

tip.

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" language="java"%>

jsp에서 한글을 썼는데 안 보인다? 외계 문자로 나온다?면 붙여볼 것

 

이렇게 모든 코드를 붙였다면 실행해보자.

 

 

- 결과 -

 

10개씩일때

 

20개일때

 

잘 적용된 것을 볼 수 있다.