글 읽기에서는 파일을 다운로드 받을 수 있도록 해주면 된다.


파일을 다운로드 받을 수 있게 해주는 것은 글 수정에서 처리 되었다.

글 수정에서 사용된 서비스와 방법을 그대로 사용하면 된다.

@RequestMapping(value = "/board4Read")
       public String board4Read(HttpServletRequest request, ModelMap modelMap) throws Exception {
       
        String brdno = request.getParameter("brdno");
       
        boardSvc.updateBoard4Read(brdno);
        boardVO boardInfo = boardSvc.selectBoardOne(brdno);
        List<?> listview = boardSvc.selectBoard4FileList(brdno);
       
        modelMap.addAttribute("boardInfo", boardInfo);
        modelMap.addAttribute("listview", listview);
       
        return "board4/boardRead";
    }

board4Ctr.java

다운로드 받도록 하는 링크를 생성하는 방법은 글 수정과 동일하지만 파일 삭제를 위해 제공한 체크 박스가 없다는 차이가 있다.

<tr>
    <td>첨부</td>
    <td>
        <c:forEach var="listview" items="${listview}" varStatus="status">   
            <a href="fileDownload?filename=<c:out value="${listview.filename}"/>&downname=<c:out value="${listview.realname }"/>">
            <c:out value="${listview.filename}"/></a> <c:out value="${listview.size2String()}"/><br/>
        </c:forEach>                   
    </td>
</tr>

boardRead.jsp

글 삭제에 따른 파일 처리는 별도로 하지 않았다.

글 삭제로 게시물이 삭제되면 별도의 처리를 하지 않아도 관계된 파일들이 화면에 나오지 않기 때문에 다른 처리를 하지 않았다. 

실제 파일도 삭제 하지 않았는데, 필요한 경우 컨트롤(boardDelete)에 파일 삭제 처리를 넣어주면 된다.








'Java > 게시판 4:자료실' 카테고리의 다른 글

1. 자료실 - 준비 & 시작  (1) 2016.04.02
2. 자료실 - 글쓰기  (0) 2016.03.28
3. 자료실 - 글 수정  (3) 2016.03.28
4. 자료실 - 리스트  (0) 2016.03.28

페이징 처리는 리스트 형식으로 데이터를 보여주는 모든 페이지에 사용되는 기능이다.

그래서, 자바에서는 PageVO로 만들어서 상속받아서 사용하도록 처리했다.

HTML에서도 공통처리하고 가져다가 사용하면 개발 시간을 단축하는 효과를 볼 수 있다.

공통으로 처리하기 위해서

공통부분을 별도의 JSP 파일로 빼고 해당 파일을 jsp:include로 가져오도록 한다.


하지만 앞서 검색 기능을 추가하였다.

현재는 검색 후 결과에 대하여 페이징이 되지 않는다.

이 문제를 해결하는 방법은

검색 폼의 내용과 페이지 번호가 같이 서버로 전송되어야 한다는 것이다.

즉, 기존에 처리한 페이징의 링크는 URL에 파라메터로 페이지 번호가 넘어가고(board3List?page=1)

검색은 폼의 입력 값들을 따로 만들어서 전송하기 때문에 같이 전송되지 않는다.

따라서, 페이징을 위해

현재 보는 페이지 번호가 폼과 같이 전송되게 하는 것이 가장 쉽다 (반대로 하면 처리가 복잡하다). 

Hidden 필드로 페이지(name=page)를 추가하고

링크가 아닌 자바 스크립트로 호출하게 처리하는 것이다.

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<c:if test="${searchVO.totPage>1}">
    <div class="paging">
        <c:if test="${searchVO.page>1}">
            <a href="javascript:fnSubmitForm(1);">[처음]</a>
            <a href="javascript:fnSubmitForm(${searchVO.page-1});">[이전]</a>
        </c:if>
               
        <c:forEach var="i" begin="${searchVO.pageStart}" end="${searchVO.pageEnd}" step="1">
            <c:choose>
                <c:when test="${i eq searchVO.page}">
                    <c:out value="${i}"/>
                </c:when>
                <c:otherwise>
                    <a href="javascript:fnSubmitForm(${i});"><c:out value="${i}"/></a>
                </c:otherwise>
            </c:choose>
            <c:if test="${not status.last}">|</c:if>
        </c:forEach>
       
    <c:if test="${searchVO.totPage > searchVO.page}">
        <a href="javascript:fnSubmitForm(${searchVO.page+1});">[다음]</a>
        <a href="javascript:fnSubmitForm(${searchVO.totPage});">[마지막]</a>
    </c:if>
    </div>
    <br/>
       
    <input type="hidden" name="page" id="page" value="" />
       
    <script type="text/javascript">
    function fnSubmitForm(page){
        document.form1.page.value=page;
        document.form1.submit();
    }
    </script>
</c:if>

common/pagingforSubmit.jsp

코드에서 확인 할 수 있듯이

페이징 관련 모든 링크는 fnSubmitForm 함수를 가고자 하는 페이지 번호와 함께 호출한다.

이 함수는 hidden 테그로 생성한 page에 값을 넣고(document.form1.page.value=page)

서버로 전송한다 (submit).

이때 폼 이름이 form1으로 검색에서 사용되는 폼 이름과 같다.


설명이 길었지만 코드는 위와 같이 간단하다.

페이징 처리에 추가된 코드는 이전(page-1), 다음(page+1), 처음으로(1),

마지막(totPage)으로 기능을 넣은 것이다.

화면에 보이는 페이지는 10개의 페이지 이다.

다음 10개 페이지(pageEnd+1), 이전 10개 페이지(pageStart-1)도 만들어 보면 좋을 것이다.


기존 JSP(boardList.jsp)파일에서는 페이징 관련 코드들을 지우고,

폼 테그 안 쪽에 페이징 파일을 가져(include) 오는 코딩을 해 준다.

<form id="form1" name="form1"  method="post">
    <jsp:include page="/WEB-INF/jsp/common/pagingforSubmit.jsp" />
    <div>
        <input type="checkbox" name="searchType" value="brdtitle" <c:if test="${fn:indexOf(searchVO.searchType, 'brdtitle')!=-1}">checked="checked"</c:if>/>
          ~~ 생략 ~~
    </div>
</form> 

boardList.jsp

이상의 내용과 게시판 2에서 정리한 내용을 합하여 정리하면

컨트롤에서 pageVO.pageCalculate를 호출하고

JSP에서 pagingforSubmit.jsp만 호출하면

간단하게 페이징 처리가 된다는 것이다.

페이징 처리는 리스트 기능에 거의 사용되는 것이니 알고 있는 것이 좋다.


현재의 리스트는 전체 데이터를 출력한다.

이것이 실제 사용될 경우 많은 데이터로 인해 문제를 발생시킬 수 있다.

즉, 예제는 몇 건 내지 몇 십 건의 데이터를 처리하기 때문에 문제가 없지만

실제 사용될 경우 몇 만에서 몇 십만의 데이터가 저장되기 때문에 한 페이지 실행되는데 아주 오랜 시간이 걸리고

웹 브라우저가 실행할 때도 메모리 부족 등의 문제가 발생할 수 있다.

따라서, 필요한 데이터만 가져오는 방식으로 처리해야 한다.

데이터가 얼마가 저장되어 있든지 필요로 하는 개수(화면에 출력하고자 하는 개수)만큼 가져오면

데이터 양과 관계없이 일정한 속도를 유지하게 된다.

이러한 처리를 페이징(Paging) 처리하고 한다.

이 부분은 많은 개발자들이 자주 실수하는 부분이며 웹 사이트 성능 저하의 주범이기도 하다.


다음과 같이 전체 데이터 개수가 9개가 있고 웹 페이지에는 3개씩 보여 준다고 가정한다.

1           

2           

3           

4           

5           

6           

7           

8           

9          

전체 페이지 수는 9 / 3으로 3이다.

첫 페이지의 데이터는 레코드 번호가 1부터 3까지

두 번째 페이지의 데이터는 레코드 번호가 4부터 6까지

세 번째 페이지의 데이터는 레코드 번호가 7부터 9가 된다.

여기에서 공식이 나오게 된다.

현재 페이지에 보여줄 데이터의 시작 번호 = (현재 페이지 – 1) * 출력 개수 + 1
현재 페이지에 보여줄 데이터의 종료 번호 = 시작 번호 + 출력개수  - 1


위 공식에 실제 페이지를 대입해 보면 

1 페이지의 시작 페이지는 = (1-1)* 3 + 1 = 1, 종료 페이지는 1 + 3 – 1 = 3
2 페이지의 시작 페이지는 = (2-1)* 3 + 1 = 4, 종료 페이지는 4 + 3 – 1 = 6
3 페이지의 시작 페이지는 = (3-1)* 3 + 1 = 7, 종료 페이지는 7 + 3 – 1 = 9

따라서, 다음과 같이 SQL 문이 실행되도록 만들면 되는 것이다.

MariaDB의 Limit는 주어진 레코드 번호 값에 해당하는 데이터를 가지고 오고, Oracle이나 MS-SQL은 Row_number나 TOP등의 명령어가 제공된다.

SELECT BRDNO, BRDTITLE,  ~~ 생략 ~~
          FROM TBL_BOARD
         WHERE BRDDELETEFLAG='N'
         ORDER BY BRDNO DESC
         LIMIT 1, 3

SELECT BRDNO, BRDTITLE,  ~~ 생략 ~~
          FROM TBL_BOARD
         WHERE BRDDELETEFLAG='N'
         ORDER BY BRDNO DESC
         LIMIT 4, 6

SELECT BRDNO, BRDTITLE,  ~~ 생략 ~~
          FROM TBL_BOARD
         WHERE BRDDELETEFLAG='N'
         ORDER BY BRDNO DESC
         LIMIT 7, 9

이렇게 데이터를 가지고 오고,

리스트의 하단에 전체 페이지 리스트를 보여 주어 사용자가 선택하도록 하면 된다.

페이지 리스트는 전체 데이터 수(9)를 구해서

보여주고자 하는 개수(3)로 나누어 전체 페이지 수(3)를 구하고

그 수만큼 반복해서 링크를 생성한다.


이상의 개념을 이해한 경우 직접 만들어서 사용하면 되고,

그렇지 못해도 문제는 없다.

Common 폴더에 PageVO 클래스에 만들어 두었기 때문에 가져다 사용하면 된다.

public class PageVO {
    private Integer displayRowCount=10;      // 출력할 데이터 개수
    private Integer rowStart, rowEnd;         // 시작행번호, 종료행 번호
    private Integer totPage, totRow=0,        // 전체 페이수, 전체 데이터 수
                         page, pageStart, pageEnd;    // 현재 페이지, 시작페이지, 종료페이지

    public void pageCalculate(Integer total) {
        getPage();
        totRow  = total;
        totPage    = (int) ( total / displayRowCount );
       
        if ( total % displayRowCount > 0 ) totPage++;

        pageStart = (page - (page - 1) % 10) ;
        pageEnd = pageStart + 9;
        if (pageEnd > totPage) pageEnd = totPage;
       
        rowStart = ((page - 1) * displayRowCount) + 1 ;
        rowEnd = rowStart + displayRowCount -1;
    }
~~ 생략 ~~

이 코드를 간단하게 설명하면 전체 데이터 개수(total)를 받아서 totRow에 보관한다.

전체 데이터수를 출력하고자 하는 개수(displayRowCount)로 나누어

전체 페이지(totPage)를 계산한다.

무조건 올림을 해야 정확한 페이지 수를 얻을 수 있다.

예로 데이터 수가 21 개 일 때 단순 나누기를 하면 2페이지가 나온다.

무조건 올림을 해야 정확한 3페이지를 얻을 수 있다.

그래서 나눈 후 나머지가 있으면 전체 페이지(totPage) 값을 1증가 시킨다.

시작 행 번호(rowStart)와 종료 행 번호(rowEnd)는 앞서 언급한 공식을 이용하여 생성하였다. 

시작페이지(pageStart), 종료페이지(pageEnd)는 설명하지 않는다.

다만, 데이터가 수천, 수 만 일 경우 페이지도 수백, 수천이 된다.

모든 페이지를 보여 줄 수 없기 때문에

페이지 리스트에 대한 페이징 처리를 하게 되고 이것을 의미한다.

원리는 같지만 이상의 것만으로 복잡해서 넘어간다.

차후 코드를 살펴 보기 바란다.


먼저, 컨트롤을 수정한다.

@RequestMapping(value = "/board2List")
public String boardList(PageVO pageVO, ModelMap modelMap) throws Exception {

        pageVO.pageCalculate( boardSvc.selectBoardCount() ); // startRow, endRow

        List<?> listview   = boardSvc.selectBoardList(pageVO);
       
        modelMap.addAttribute("listview", listview);
        modelMap.addAttribute("pageVO", pageVO);

        return "board2/boardList";
}

board2Ctr.java

board2List의 파라메터로 PageVO 클래스 값을 받아오는 것은

사용자가 선택한 페이지 값을 알기 위한 것이다.

값이 없는 경우(Null) 1 페이지로 처리한다.

앞서 언급한 데로 PageVO에 메소드로 pageCalculate가 정의 되어 있기 때문에

서비스에서 전체 데이터 개수(COUNT)를 구하는 SQL문을 실행한다.

<select id="selectBoard2Count" resultType="Integer" >
        SELECT COUNT(*)
          FROM TBL_BOARD
         WHERE BRDDELETEFLAG='N'
</select>

Board2.xml

public Integer selectBoardCount() throws Exception {
    return sqlSession.selectOne("selectBoard2Count");
}

board2Svc.java

pageVO에서 시작 행 번호(rowStart)와 종료 행 번호(rowEnd)를 계산해서(pageCalculate) 가지고 있기 때문에,

selectBoardList를 호출 할때 파라메터로 넘겨준다.

이 값들은 다음과 같이 사용된다.

    <select id="selectBoard2List" resultType="gu.board2.boardVO" parameterType="gu.common.PageVO">
        SELECT BRDNO, BRDTITLE, BRDWRITER, DATE_FORMAT(BRDDATE,'%Y-%m-%d') BRDDATE, BRDHIT
          FROM TBL_BOARD
         WHERE BRDDELETEFLAG='N'
         ORDER BY BRDNO DESC
         LIMIT ${rowStart-1}, 10 (주의)
    </select>

board2.xml

public List<?> selectBoardList(PageVO param) throws Exception {
    return sqlSession.selectList("selectBoard2List", param);
}

board2Svc.java

이상의 처리로 페이징 처리는 끝났다.

10개 이상의 데이터를 넣고 실행해보면 10 개만 출력되는 것을 알 수 있다.



  주의: 앞서 필요한 데이터를 가져오는 부분에 대한 설명에 있어서

LIMIT 1, 3

~~

LIMIT 4, 6

~~

LIMIT 7, 9 는

첫 번째부터 세 번째, 네 번째부터 여섯 번째 등으로 설명하기 하기 위한 것이고

MariaDB(Mysql)의 LIMIT 문은

첫 번째부터 세 개, 네 번째부터 세 개로 사용해야 한다.

따라서

LIMIT 1, 3

~~

LIMIT 4, 3

~~

LIMIT 7, 3 으로 사용해야 하고

앞서의 Mybatis SQL문(board2.xml)에서는 그렇게 사용하고 있다.

Oracle이나 MS-SQL에서 Row_number를 사용할 경우에는

앞서의 설명대로 사용하여 LIMIT ${rowStart-1}, ${rowEnd}로 표현해서 사용한다.



이제 JSP에서 리스트 하단에 페이지 번호들이 나오게 처리해야 하다.

시작페이지(pageStart)부터 종료페이지(pageEnd)까지 반복하면서(forEach)하면 그 값을 링크를 걸어서 출력(out)하면 된다.

              </c:forEach>
        </tbody>
    </table>
<c:if test="${pageVO.totPage>1}">
<div class="paging">
    <c:forEach var="i" begin="${pageVO.pageStart}" end="${pageVO.pageEnd}" step="1">
        <c:url var="pageLink" value="board2List">
        <c:param name="page" value="${i}" />
        </c:url>                       
            <a href="${pageLink}"><c:out value="${i}"/></a>
    </c:forEach>
</div>
</c:if>  

boardList.jsp

이 코드에서 현재 페이지(paveVO.page)는 링크를 걸지 않게 만들면 더 좋을 것이니 직접 해 보길 바란다.


게시물들은 등록된 후 수정되고 삭제 된다.

삭제 된 경우 리스트에서 보여 주는 글 번호가 들쑥날쭉 보이면서 뭔가 문제가 있는 것처럼 보인다. 

아래의 계산식을 사용하면 깨끗하게 이어진 새로운 번호가 보이게 된다. 

다음 코드를 사용해 보고 계산식은 각자 확인해 보길 바란다.

<tr>
    <td>
        <c:out value="${pageVO.totRow-((pageVO.page-1)*pageVO.displayRowCount + status.index)}"/>                   
    </td>
    <td><a href="${link}"><c:out value="${listview.brdtitle}"/></a></td>
    <td><c:out value="${listview.brdwriter}"/></td>
    <td><c:out value="${listview.brddate}"/></td>
    <td><c:out value="${listview.brdhit}"/></td>
</tr>

boardList.jsp















+ Recent posts