본문 바로가기
Framework/MyBatis

[02] 페이징처리 : RowBounds

by 예스p 2023. 2. 14.

페이징처리

이전 레거시 방식에서는 페이징처리된 목록을 조회해올때

rownum으로 정렬하기위해 인라인 안에 인라인을 넣어야만 했었다.
해당 과정을 Mybatis에서 제공하는 RowBounds을 사용하면 간단하게 처리할 수 있다.

또한 페이징 데이터들을 구할때 모듈화(메소드로 정리하는것)를 활용해보자.


코드 리펙토링/ 코드모듈화

  • 페이징처리에 필요한 데이터들은 다음과 같다
    listCount : 총게시글 수 (select문으로 조회 필요)
    currentPage : 요청한 페이지 번호 (클라이언트의요청에 따름)
    pageLimit : 페이징바의 페이지갯수 (설계자가 정한다)
    boardLimit : 한페이지에 보이는 게시글 갯수 (설계자가 정한다)
    maxPage : 총 페이지 수
    startPage : 페이징바의 시작 수
    endPage : 페이징바의 끝 수
  • 이때 마지막 3개 데이터는 위 4개 데이터를 연산하여 구한다.
  • Pagination 클래스를 만들어서 해당과정을 static 메소드로 정리해보자.
public class Pagination {
    public static PageInfo getPageInfo(int listCount, int currentPage, int pageLimit, int boardLimit) {
        int maxPage = (int)Math.ceil((double)listCount/boardLimit);
        int startPage = (currentPage-1)/pageLimit * pageLimit +1;
        int endPage = startPage+pageLimit -1;
        if(endPage>maxPage) {
            endPage = maxPage;
        };
        PageInfo pi = new PageInfo(listCount, currentPage, pageLimit, boardLimit, maxPage, startPage, endPage);
        return pi;
    }
}

 

 

RowBounds 페이징을 위한 준비

  • Controller 단
    1. 페이징을 위한 조회(listCount, 총 게시글 수)를 해온다.
    2. getPageInfo() 메소드를 통해 PageInfo 객체를 만든다.
    3. 목록을 조회할 Sercive메소드를 호출하며 PageInfo 객체를 보낸다.
    4. PageInfo 객체와 ArrayList<Board> 객체를 request에 담은 후 포워딩 한다.
  • Service 단
    1. SqlSession 객체를 만든후 PageInfo객체와 함께 Dao메소드에 보낸다.
  • Dao 단
    1. PageInfo객체의 데이터를 이용해서 offset과 limit의 데이터를 만든다.
    2. offset과 limit을 매개변수로 넣어 RowBounds객체를 만든다.
    3. selectList(String, Object, RowBounds) 메소드를 실행한다.
  • mapper 단
    1. 전체 리스트를 조회해오는 select문을 작성한다.
    (board_no기준으로 내림차순하는것을 잊지 않는다)

 

 

Dao상세 : RowBoundsselectList()

  • RowBounds :
    Mybatis에서 페이징처리를 위해 제공하는 클래스.
    RowBounds객체 생성시, 몇개의 데이터를 건너뛰고 몇개의 데이터를 조회할지 설정 가능하다.
    new RowBounds(offsetlimit)
  • offset :
    - 몇개의 게시글을 건너뛸건지에 대한 값
    = (currentPage-1)*boardLimit
  • limit :
    - 몇개의 게시글을 조회할건지에 대한 값
    = boardLimit
currentPage =>
boardLimit이 5일때 보이는 글번호
offset(건너뛸 숫자) limit(조회할 숫자)
1 => 1~5번글 조회 0 5
2 => 6~10번글 조회 5 5
3 => 11~15번글 조회 10 5
결론 (currentPage-1)*boardLimit boardLimit
  • selectList(String, Object, RowBounds) :
    selectList()는 매개변수 3개짜리가 오버로딩 되어있어서 RowBounds객체를 보낼 수 있다.
    만약 sql이 완성되어 객체를 넘길필요가 없다면 두번째 자리에 null을 쓰면 된다.
    ex) selectList("boardMapper.selectList", null, rowBounds);
  • selectList는 List<Object>로 반환하기 때문에 ArrayList로 강제형변환 해주는 것을 잊지말자
public ArrayList<Board> selectSearchList(SqlSession sqlSession, HashMap<String, String> map, PageInfo pi){
	int offset = (pi.getCurrentPage()-1)*pi.getBoardLimit();
	int limit = pi.getBoardLimit();
	RowBounds rowBounds = new RowBounds(offset, limit);
	return (ArrayList)sqlSession.selectList("boardMapper.selectSearchList", map, rowBounds);
}

 

 

ROWNUM vs RowBounds

  • 기존 ROWNUM방식과 마이바티스의 RowBounds방식은 각각의 장단점이 있다.
  ROWNUM RowBounds
장점 대량의 데이터도 빠르게 페이징 처리를 하여 가져올 수 있다. 구현이 쉽고, 코드의 유지보수가 간편하다
단점 페이징 처리를 구현하기 위한 코드가 복잡하다. 대량의 데이터를 사용할 경우 수행속도가 늦다

 

 


페이징: 화면꾸미기

가지고 온 데이터를 바탕으로 페이징 화면을 구현하는것을 연습해보자.

만약 검색결과를 나타내는 페이징데이터가 있을경우,

화면을 어떻게 구성해야하는지도 알아본다.


기본 : 단일 페이징 화면

  • 검색기능이 없는 목록화면을 꾸며본다.
  • 이때, 액션태그와 EL구문을 사용해서 자바구문 없이 코드를 짜보도록 한다.
<!-- 목록 영역 -->
<c:forEach var="b" items="${ list }">
    <tr>
        <td>${ b.boardNo }</td>
        <td>${ b.boardTitle }</td>
        <td>${ b.boardWriter }</td>
        <td>${ b.count }</td>
        <td>${ b.createDate }</td>
    </tr>
</c:forEach>
...
<!-- 페이징 영역 -->
<c:if test="${ pi.currentPage ne 1 }"> <!-- 내가 보고있는 페이지가 1이 아닐경우 -->
    <a href="list.bo?cpage=${ pi.currentPage -1 }">[이전]</a>
</c:if>
			
<c:forEach var="p" begin="${ pi.startPage }" end="${ pi.endPage }">
    <a href="list.bo?cpage=${ p }">${ p }</a>
</c:forEach>
            
<c:if test="${ pi.currentPage ne pi.maxPage }"> <!-- 내가 보고있는 페이지가 마지막페이지가 아닐경우 -->
    <a href="list.bo?cpage=${ pi.currentPage +1 }">[다음]</a>
</c:if>

 

 

심화 : 검색 페이징이 덧붙여진 화면

  • 목록화면과 검색결과화면이 동일하다면 목록과 페이징바의 구성요소를 공유하게 된다.
    >> request에 데이터를 담을때 동일한 키값으로 담는다.
    (검색결과list==목록list, 검색결과pageInfo==목록pageInfo)
  • 검색 결과를 표시할 jsp단에서는 2가지 조정이 필요하다.
    1. 사용자가 선택한 검색조건과 검색어를 표시한다.
       >> controller단에서 condition과 keyword를 request에 담아놓은 후 화면에서 활용한다.
    2. 일반목록의 기능만 하는 페이징바를 검색목록의 기능도 하도록 바꾸어야한다.
       >> request에 condition이 들어있느냐 여부로 기능을 스위치한다.
<!-- ----------사용자가 입력한 검색어 표기하기---------- -->
<input type="text" name="keyword" value="${ keyword }">
<!-- ----------사용자가 선택한 검색조건 표기하기---------- -->
<script>
	document.querySelector("#search-area option[value=${condition}]").selected = true;
</script>
<!-- ----------페이징바를 동적으로 스위치하기---------- -->
<div id="paging-area">
<c:choose>
    <c:when test="${ empty condition }"> <!-- 일반목록인경우 -->
        <c:if test="${ pi.currentPage ne 1 }">
        <a href="list.bo?cpage=${ pi.currentPage -1 }">[이전]</a>
        </c:if>

        <c:forEach var="p" begin="${ pi.startPage }" end="${ pi.endPage }">	
            <a href="list.bo?cpage=${ p }">[${ p }]</a>
        </c:forEach>

        <c:if test="${ pi.currentPage ne pi.maxPage }"> 
        <a href="list.bo?cpage=${ pi.currentPage +1 }">[다음]</a>
        </c:if>
    </c:when>
    <c:otherwise> <!-- 검색목록인경우 -->
        <c:if test="${ pi.currentPage ne 1 }">
        <a href="search.bo?cpage=${ pi.currentPage -1 }&condition=${condition}&condition=${keyword}">[이전]</a>
        </c:if>

        <c:forEach var="p" begin="${ pi.startPage }" end="${ pi.endPage }">	
            <a href="search.bo?cpage=${ p }&condition=${condition}&condition=${keyword}">[${ p }]</a>
        </c:forEach>

        <c:if test="${ pi.currentPage ne pi.maxPage }"> 
        <a href="list.bo?cpage=${ pi.currentPage +1 }&condition=${condition}&condition=${keyword}">[다음]</a>
        </c:if>
    </c:otherwise>
</c:choose>	
</div>

 

 

 

 

 

 

 

 

이미지 출처 instagram @teaaalexis

댓글