본문 바로가기
Lecture/Mybatis

[본격 게시판짜기 Part2.7 Model2 MVC패턴] i-batis의 queryForList를 활용한 Paging기능구현

by cusmaker 2012. 7. 21.
반응형


2012/06/13 - [Lecture/HTML] - [본격 게시판짜기 Part1.1 - 게시판도 HTML부터] 게시글 리스트

2012/06/13 - [Lecture/HTML] - [본격 게시판짜기 Part1.2 - 게시판도 HTML부터] 글입력폼

2012/06/13 - [Lecture/Javascript-기초] - [본격 게시판짜기 Part1.3 - Dom 맛보기 ] 글입력폼 검사

2012/06/13 - [Lecture/Jsp] - [본격 게시판짜기 Part1.4 - HTML-> JSP] form 파라미터 받기

2012/06/26 - [Lecture/SQL / Oracle] - [본격 게시판짜기 Part1.5 JSP > Oracle] 게시판 DB 테이블 생성

2012/06/26 - [Lecture/Jsp] - [본격 게시판짜기 Part1.6 Oracle > JSP] Database 접속 및 Select

2012/07/05 - [Lecture/Jsp] - [본격 게시판짜기 Part1.7 JSP, SQLDeveloper] 게시글 입력 및 리스트조회기능

2012/07/08 - [Lecture/Jsp] - [본격 게시판짜기 Part1.8 JSP 게시글 조회] 게시글 조회기능 및 게시글 삭제

2012/07/10 - [Lecture/Jsp] - [본격 게시판짜기 Part2.1 Model2 MVC패턴] 뷰(View) 코드 분리

2012/07/10 - [Lecture/Jsp] - [본격 게시판짜기 Part2.2 Model2 MVC패턴] Entity Beans의 사용

2012/07/12 - [Lecture/Jsp] - [본격 게시판짜기 Part2.3 Model2 MVC패턴] Controller구성

2012/07/13 - [Lecture/Jsp] - [본격 게시판짜기 Part2.4 Model2 MVC패턴] Model 구성

2012/07/16 - [Lecture/Mybatis] - [본격 게시판짜기 Part2.5 Model2 MVC패턴] Model 구성2 - i-batis의 사용

2012/07/17 - [Lecture/Mybatis] - [본격 게시판짜기 Part2.6 Model2 MVC패턴] Model 구성2 - i-batis의 사용2


안녕하세요 cocy입니다.

이번시간에는 아이바티스의 queryForList메서드를 활용한 페이징기능 구현을 해보도록 하겠습니다.

페이징 기능은 프로그래머, 데이터베이스별로 구현방식이 조금씩 달라질 수 있는 테크니컬한 내용인데요,

기능구현에 정답은 없으니, 이렇게도 구현 하는구나.. 하고 이해만 하고 넘어가시면 되겠습니다.


일단 페이징 기능을 구현하기위해서 기능을 단순히 정의하자면,

모든 게시글에대해 정해진 게시글 갯수만큼 페이지번호를 매겨 뿌려준다!

이정도면 되겠네요.

이 기능을 구현할때 크게 세가지파트에서 구현방식이 달라질 수 있겟는데요,

1. 모든 리스트를 가져와서 뷰에서 자바스크립트를 이용하여 잘라서 보여준다.

> 비효율적이고, 자바스크립트로 dom을 제어하는 부분을 아직 많이 배우지 못했으므로 패스

2. 자바에서 가져온 리스트를 잘라서 보여준다

> 역시 모든 데이터를 가져오기에 비효율적이며, 구현이 막막합니다.

3. DB에 날리는 질의에 파라미터를 주고 조건에 해당하는 게시글들만 가져오게 한다.

> 성능상 가장 탁월하며 널리쓰이지만, 역시 구현하기에 너무 복잡하고, 복잡한 쿼리가 필요합니다.


그래서 어떤방식으로 구현할 것이냐! 2번입니다. 

페이징 기능이 흔하고 하도 많이들 쓰여서 그런지(?)

아이바티스에는 이를 위한 메서드가 존재합니다.

바로 queryForList입니다.

눈에 익지 않나요?

우리는 이미 BoardDao에서 이 메서드를 사용한 바 있습니다.

다만 파라미터 2개짜리 메서드를 사용했는데요,

2개짜리 메서드에서는 첫번째 파라미터로 쿼리를 식별할 수 있는 id, 

두번째는 쿼리로 파라미터를 넘기기위한 파라미터였습니다. 


그런데 저희가 사용할 queryForList는 

파라미터 4개짜리도 오버로딩되어있습니다.

앞의 두개의 파라미터는 동일하고,

세번째 파라미터는 가져올 페이지번호,

네번째 파라미터는 가져올 데이터 로우의 갯수입니다.


그럼 설명은 이쯤하고 코드 수정에 들어갑시다.

좌표는 com.board.dao의 BoardDao.Java

@SuppressWarnings("unchecked")
 
public ArrayList<Board> getArticleList() throws SQLException  {
 
    return  (ArrayList<Board>)GetDB().queryForList("getArticleList"null010 );
 
}


기존 소스에 파라미터 두개만 집어 넣었습니다.

어떻게 동작하는지 확인해보시죠!

사진1. queryForList 페이징 파라미터 추가

게시글 갯수가 줄어들엇습니다.

그런데 확인해보시면 알겠지만 세번째 파라미터가 페이지번호라고 말씀드렸는데요,

0을 넣으면 질의한 쿼리의 반환값에서 0개를 제외한 다음 10개를 가져오는 것입니다.

즉 다음 페이지(2페이지, 즉 23번 게시글 이후 10개)를 보려면 

현재 페이지번호 + 게시글 갯수(즉 10)를 넣어야 된다는 것입니다. 이해되셨나요?


그럼 여기서 조금 더 생각해보자면 한가지 사실을 알 수 있습니다. 위에서 설명했지만 

queryForList메서드는 일단 모든 게시글을 가져온뒤에 내부적으로 결과를 잘라서 보여주는 것이므로,

게시글 즉, DB데이터의 Row 갯수가 방대해지면 그만큼 속도와 성능에 영향을 끼친다는 것입니다!

만약에 앞으로 여러분이 데이터의 규모가 방대하고, 사용자가 많은 시스템을 개발한다고 하면, 

이방법으로 페이징하는것은 비추합니다. (3번으로 개발!)


그럼 다음으로 어떤식으로 작동하는지 알았으니 본격적으로 Dao, Action과 뷰에도 기능을 추가해봅시다. 

우선 Dao에서는 세번째 파라미터를 0으로 하드코딩된값을 대체하기위해 

메서드에 int형식의 파라미터를 추가합시다.

(게시글의 갯수또한 동적으로 구성하려면 파라미터로 구성하셔도 되지만 지금은 10으로 고정하겠습니다.)

@SuppressWarnings("unchecked")
 
public ArrayList<Board> getArticleList(int page) throws SQLException  {
 
    return  (ArrayList<Board>)GetDB().queryForList("getArticleList"null, page, 10 );
 
}

그럼 이 메서드를 호출하는 ListAction에서도 수정해 줘야겠죠?

ListAction에서는 url로부터 page파라미터를 받아야하는데....
벌써부터 list.do를 호출하는 놈을 전부 찾아다가 바꾸기엔 너무 귀찮습니다. 실수할 것 같기도 하고,,,

그럼 page변수를 선언하고 넘어오는 파라미터가 없으면 기본페이지를 0으로 하면 어떨까요?

이게 바로 예외처리의 시작이자 형상관리의 시작입니다. 수정된 코드를 봅시다.


public class ListAction implements CommandAction {
 
    @Override
 
    public String requestPro(HttpServletRequest request,
 
            HttpServletResponse response) throws Throwable {
 
        int page = 0;                                            // 기본 페이지번호를 0으로 설정하고
 
        if(request.getParameter("page") != null){     // 넘어온 파라미터가 있다면 
 
            page = Integer.parseInt(request.getParameter("page"));  
 
        }                                // 해당 파라미터를 int형으로 캐스팅후 변수에 대입합니다.
 
            ArrayList<Board> articleList = BoardDao.getInstance().getArticleList(page);  
 
// 그리고 변경된 dao 메서드에 넣어줍니다.
 
            request.setAttribute("articleList", articleList);   // 셋팅된 리스트를 뷰에 포워드합니다.
 
request.setAttribute("page", page); // 페이지번호를 뷰에서 보기위해 표시합니다.
 
        return "list.jsp";
 
        }
 
}

단순하죠? 
그럼 다음에 할일은? 뷰에서 페이지번호를 구현해주는 일입니다.
그런데 보통의 페이징기능을 생각할 경우,

1. 제일 최근리스트를 보여주는 버튼         ex) <<
2. 바로 이전리스트를 보여주는 버튼         ex) <
3. 모든 게시글의 글번호들                      ex) 1 2 3 4 5 6 7 8 9 10
4. 바로 다음 리스트를 보여주는 버튼        ex) >
5. 제일 마지막 리스트를 보여주는 버튼     ex) >>

1번은 그냥 list.do를 호출하면 되고..
2번도 그냥 list.do에 파라미터를 현재 페이지 - 글갯수(0보다 커야함)로 주면 되고
4번도 역시 list.do에 파라미터를 현재 페이지 + 글갯수로 주면 되는데,,,

3번과 5번 이 두 연산을 하려면 가장 중요한것이 바로 모든 게시글의 갯수입니다.
그러기 위해서는 
모든 게시글의 갯수를 가져오는 쿼리를 날리는 메서드를 dao에 추가하고
해당 변수를 파라미터에 추가하고,... 수정하기전에는 모든게시글을 가져왔으니 사이즈만 조사하면 됐는데....
벌써부터 머리가 아파오기 시작합니다.

그래서 너무 포스팅이 길어질것같기도 하니 과감히 생략하고 
이전페이지, 다음페이지 이 두가지만 추가하도록 합시다.
뭔가 부족하신분들은 따로 구현해보세요 :)

그럼 이제 list.jsp를 봅시다.
코드를 집어넣을 좌표는 테이블과 글쓰기 버튼 사이로 합시다.


<h1>게시글 리스트</h1>                <!-- 헤드라인 글씨를 표현하는 태그입니다. -->
<table>                           <!-- 표 형식의 데이터를 표현하는 태그입니다. -->
    <tr>                          <!-- table태그 내에서 행을 정의할때 쓰는 태그입니다. -->
        <th>번호</th>             <!-- Table Header의 약자로 table태그 내에서 -->
        <th>제목</th>             <!-- 강조하고싶은 컬럼을 나타낼때 쓰는 태그입니다. -->
        <th>작성자</th>
        <th>날짜</th>
        <th>조회수</th>
    </tr>
    <c:forEach items="${articleList}" var="article">
        <tr>
            <td>${article.idx}</td>
            <td><a href='content.jsp?idx=${article.idx}'>${article.title} </a></td>
            <td>${article.writer}</td>
            <td>${article.regdate}</td>
            <td>${article.count}</td>
        </tr>
    </c:forEach>
</table>
 
<a href="list.do?page=${page-10}">이전페이지</a>
${page/10+1 } 페이지
<a href="list.do?page=${page+10}">다음페이지</a>
 
<br/><a href="write.jsp">글쓰기</a>

빨갛게 표시된 내용이 추가된 내용입니다.
실행해보시면 돌아가긴 하지만 매우 형편없습니다. 
페이지번호가 -로 가기도 하고, 현재 페이지가 소숫점으로 표시되기도하고..
아직 예외처리가 안되었기때문인데요,
이전페이지에서는 page가 0이하일 경우에는 page에서 10을 못빼게 해버리고
다음페이지에서는 현재 게시글이 10개 이하일경우에 역시 10을 못더하게 해버립시다.
다음페이지기능 구현에서 현재 가진 조건들로만 페이지를 구성해야하려다 보니 
다음페이지가 10개일경우엔 빈 페이지가 표시됩니다.

이때 jstl의 <c:if test="${}"> </c:if> 구문이 필요합니다.
그리고 가져온 리스트의 사이즈를 조사하기위해 
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
이 구문을 상단에 추가해주시고 ${fn:length( articleList )} 함수를 사용합니다.
그리고 소수점처리를 하기위해 
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
구문을 마찬가지로 추가해주시고 
현재 페이지를 ${page/10+1 } 대신 

<fmt:parseNumber value="${page/10+1 }" type="number"  integerOnly="True" />
요렇게 고쳐주세요. 

그럼 이를 가지고 이전페이지를 고쳐봅시다.

<c:if test="${page > 0}"> 
<a href="list.do?page=${page-10}">이전페이지</a> 
</c:if>
<c:if test="${page == 0}"> 
<a href="#">이전페이지</a> 
</c:if>

페이지가 0보다 크면 이전페이지에 파라미터를 10을 빼서 넘기는 url을 표현합니다.
반대로 0일 경우에는 아에 그냥 요청을 못하게 해버렸습니다. 
저런식으로 a태그의 href속성에 #을 넣어주면 아무동작하지 않습니다.

그럼 이제 다음페이지를 고쳐봅시다.

<c:if test="${fn:length( articleList ) < 10}"> 
<a href="#">다음페이지</a>
</c:if>
<c:if test="${fn:length( articleList ) == 10}"> 
<a href="list.do?page=${page+10}">다음페이지</a>
</c:if>

자 간단한 페이지기능이 끝났습니다.
뭔가 기능이 밋밋하죠? 꼭 제대로 구현해보시기바랍니다.

사진2. i-batis를 활용한 간단한 페이징기능 구현


다음시간에는 카운터기능을 추가해 보도록 하겠습니다.
수고하셨습니다.