본문 바로가기

[CRUD] 페이징

2023. 11. 29.
반응형
검생 > 페이징 > Ajax > 파일 업/다운로드

 

대량의 데이터라고 불릴 만큼의 데이터가 쌓인다면 한 번에 모든 데이터를 조회하는 것은 성능, 서버 부하, 로딩 시간,  사용자 환경 모두 악영향을 끼치게 됩니다. 이 때 페이징을 통해 제한된 데이터만 보여줌으로써 성능 개선부터 사용자의 경험 향상까지 도움을 줄 수 있습니다.

페이징 시작 하기 전 간단히 페이징에 대해서 짚고 넘어가도록 하겠습니다.

페이징 예시

위의 사진을 기준으로 설명 드리겠습니다. 한 페이지에 보여줄 데이터를 정합니다. (10개, 20개, 40개, ... 등등) 그리고 위의 사진에서 1~5 페이지를 한 블럭이라고 할 수 있습니다. 이전을 누르면 이전 블럭, 다음을 누르면 다음 블럭으로 가는 것이죠. 블럭은 페이징의 페이징이라고 볼 수 있겠습니다. 페이지도 점점 늘어날텐데 한번에 다 보여주는 것은 비효율적이니까 블럭으로 나눈 것이죠.
요즘은 다양한 페이징 기법을 많이 공유하고 있으니 자신이 사용하고자 하는 방법을 구글링해서 적용하시면 됩니다.

1. 페이징 검색

원하는 기능을 적어서 다른 기능이 나올 확률을 줄입니다.

https://doublesprogramming.tistory.com/100

 

Spring - 게시판 만들기 연습(페이징 처리)

Spring - 게시판 만들기 연습(페이징 처리) 1. 실행화면01) 게시글 목록의 페이지, 다음, 이전, 처음, 끝 버튼 생성, 현재페이지 하이퍼링크 하이퍼링크제거02) 게시글 검색 후 페이지 이동시(끝페이

doublesprogramming.tistory.com

딱 제가 원하는 페이징 기법입니다!

 

2. Mapper 작성 및 수정

	<!-- 기존 코드 -->
	<sql id='searchWhere'>
        <where>
            <choose>
                <when test='searchType == "choise" '>
                	1=1
                </when>
                <when test='searchType == "title" '>
                	title like '%' || #{keyword} || '%'
                </when>
                <when test='searchType == "writer" '>
                	writer like '%' || #{keyword} || '%'
                </when>
                <when test='searchType == "titleAndContent" '>
                	(title like '%' || #{keyword} || '%' or content like '%' || #{keyword} || '%')
                </when> 
            </choose>
            <if test='startDate != null and startDate != "" '>
            	and to_char(reg_date, 'yyyy-MM-dd') between #{startDate} and #{endDate}
            </if>
        </where>
    </sql> 

    <sql id='selectAll'>
        select
            board_seq,
            writer,
            title,
            content,
            reg_date,
            upt_date,
            view_cnt
    </sql>

    <!-- 수정된 코드 -->
    <select id="showBoardList" resultMap="Board">
        <include refid='selectAll'></include>
        from (
            <include refid='selectAll'></include>,
            	row_number() ovew(order by board_seq desc) rnum
            from board
            <include refid='searchWhere'></include>
            order by board_seq desc
        )
        where rnum between #{startRow} and #{endRow}
    </select>

    <!-- 추가된 코드 -->
    <select id='boardsNum' resultType='int'>
        select 
        	count(board_seq)
        from board
        <include refid='searchWhere'></include>
    </select>

페이징을 하기 위해선 총 게시물의 개수가 필요하니 같은 조건으로 조회한 게시물 개수를 반환하는 쿼리문을 작성해 줍니다.
selectAll은 , 없이 끝나므로 row_number()와 view_cnt를 구분해주기 위해 <include>문이 끝나고 , 를 붙여줍니다.

3. Dao, DaoImpl 작성

// Dao
	// 이전 코드
	int boardsNum(Map<String, Object> param);
    
// DaoImpl
	// 이전 코드
    @Override
    public int boardsNum(Map<String, Object> param) {
    	return sqlsession.selectOne("BoardMapper.boardsNum", param);
    }

 

 

4. Service, ServiceImpl 작성

// Service
	// 이전 코드
    int boardsNum(Map<String, Object> param);

// ServiceImpl
	// 이전 코드
    @Override
    public int boardsNum(Map<String, Object> param) {
    	return boardDao.boardsNum(param);
    }

 

5. Controller 수정

@RequestMapping(value = "/list", method = RequestMethod.GET)
public String list(Model model, @RequestParam Map<String, Object> param) {
    BoardPager pager1 = new BoardPager();

    int totalCount = boardSer.boardsNum(param); // 총 게시글 수
    pager1.setTotPage(totalCount); // 총 페이지 수 setter
    int totalPage = pager1.getTotPage(); // 총 페이지 수 getter
    int pageNum = 0;
    // Default 페이지 설정
    if (param.get("pageNum") == null || Integer.parseInt(param.get("pageNum").toString()) == 1) {
    	pageNum = 1;
    } else {
    	pageNum = Integer.parseInt(param.get("pageNum").toString());
    }

    if (pageNum > totalPage)
    	pageNum = totalPage;
    if (pageNum < 1)
    	pageNum = 1;

    BoardPager pager2 = new BoardPager(totalCount, pageNum);
    int startRow = pager2.getPageBegin();
    int endRow = pager2.getPageEnd();

    param.put("startRow", startRow);
    param.put("endRow", endRow);

    List<Map<String, Object>> boardList = boardSer.getList(param);

    model.addAttribute("pager", pager2);
    model.addAttribute("boardList", boardList);

    return "listPage";
}

 

6. listPage.jsp 수정

    <main>
        <div class='container'>
            <form id='searchForm'>
                <label>상세조건</label>
                <input type="hidden" name="pageNum" id="pageNum" value="1">
                <select name='searchType'>
                    <option value='choice' ${param.searchType == 'choice' ? 'selected' : ''}>선 택</option>
                    <option value='title' ${param.searchType == 'title' ? 'selected' : ''}>제 목</option>
                    <option value='writer' ${param.searchType == 'writer' ? 'selected' : ''}>글쓴이</option>
                    <option value='titleAndContent' ${param.searchType == 'titleAndContent' ? 'selected' : ''}>제목or내용</option>
                </select>
                <input type='text' id='keyword' name='keyword' value="${param.keyword }">
                <input type="date" id="startDate" name="startDate" value="${param.startDate}">
                <input type="date" id="endDate" name="endDate" value="${param.endDate }">
                <button id='searchBtn'>검색</button>
            </form>
            <br>
            <form id='delForm'>
                <button onclick='createBoardBtn(event)'>글쓰기</button>
                <button onclick='deleteBoardBtn(event)'>삭제</button>
                <table>
                    <thead>
                        <tr>
                            <th><input type='checkbox' id='allChk'></th> <!-- 체크박스 -->
                            <th>번호</th>
                            <th>글쓴이</th>
                            <th>제목</th>
                            <th>만든 날짜</th>
                            <th>수정 날짜</th>
                            <th>조회수</th>
                        </tr>
                    </thead>

                    <tbody>
                        <c:forEach var="boardList" items="${boardList}">
                            <tr>
                                <td><input type="checkbox" name="chkBox" value="${boardList.boardSeq}"></td>
                                <td>${boardList.boardSeq}</td>
                                <td>${boardList.writer}</td>
                                <td><a href="${path}/read/${boardList.boardSeq}">${boardList.title}</a></td> <!-- title -->
                                <td><fmt:formatDate value="${boardList.regDate}" pattern="yyyy-MM-dd" /></td>
                                <td><fmt:formatDate value="${boardList.uptDate}" pattern="yyyy-MM-dd" /></td>
                                <td>${boardList.viewCnt}</td>
                            </tr>
                        </c:forEach>
                    </tbody>

                    <tfoot>
                        <tr>
                            <td colspan="7">
                            	<!-- **처음페이지로 이동 : 현재 페이지가 1보다 크면  [처음]하이퍼링크를 화면에 출력-->
                                <c:if test="${pager.curPage > 1}">
                                	<a href="javascript:list('1')">[처음]</a>
                                </c:if>

                            	<!-- **이전페이지 블록으로 이동 : 현재 페이지 블럭이 1보다 크면 [이전]하이퍼링크를 화면에 출력 -->
                                <c:if test="${pager.curBlock > 1}">
                                	<a href="javascript:list('${pager.prevPage}')">[이전]</a>
                                </c:if>

                            	<!-- **하나의 블럭에서 반복문 수행 시작페이지부터 끝페이지까지 -->
                                <c:forEach var="num" begin="${pager.blockBegin}" end="${pager.blockEnd}">
                                    <!-- **현재페이지이면 하이퍼링크 제거 -->
                                    <c:choose>
                                        <c:when test="${num == pager.curPage}">
                                        	<span style="color: red">${num}</span>&nbsp;
                                        </c:when>
                                        <c:otherwise>
                                        	<a href="javascript:list('${num}')">${num}</a>&nbsp;
                                        </c:otherwise>
                                    </c:choose>
                                </c:forEach>

                                <!-- **다음페이지 블록으로 이동 : 현재 페이지 블럭이 전체 페이지 블럭보다 작거나 같으면 [다음]하이퍼링크를 화면에 출력 -->
                                <c:if test="${pager.curBlock <= pager.totBlock}">
                                	<a href="javascript:list('${pager.nextPage}')">[다음]</a>
                                </c:if>

                                <!-- **끝페이지로 이동 : 현재 페이지가 전체 페이지보다 작거나 같으면 [끝]하이퍼링크를 화면에 출력 -->
                                <c:if test="${pager.curPage < pager.totPage}">
                                	<a href="javascript:list('${pager.totPage}')">[끝]</a>
                                </c:if>
                            </td>
                        </tr>
                    </tfoot>

                </table>
            </form>
        </div>
    </main>

기본 페이지를 위해 1의 값을 갖는 hidden type의 input을 삽입해 줍니다.
그리고 아까 검색해서 찾은 페이지에 있던 페이징 HTML(JSP) 코드를 삽입해줍니다. 

7. JQuery 작성

    function list(page){
        $("#pageNum").val(page);
        $("#searchBtn").click();
    }

 

8. 결과

기본 페이징 테스트
다음, 이전 버튼 체크
끝, 처음 버튼 체크
검색 적용 페이징 테스트

 

후기

오늘 페이징을 하면서는 자잘한 실수가 많아서 고생을 많이 했습니다.

1. <include>문 끝나고 , 를 붙이지 않아서 오류가 났는데 엉뚱하게 keyword 관련 메세지가 나와서 한동안 멘붕을 겪기도 했습니다. 다행히 함께 나오는 쿼리문을 분석하다가 ,를 쓰지 않았다는 것을 발견해서 해결할 수 있었습니다.
2. 그리고 또 엉뚱하게 잘만 되던 8080 port가 사용중이라는 에러 메세지가 떠서 Server > Poject > Ports > HTTP/1.1 의 Port Number를 8089로 바궈서 Tomcat을 구동시키는데 성공하기도 했습니다.
찾아보니 cmd에서 netstat 명령어로 사용중인 port 번호를 볼 수 있다고 해서 netstat -an으로 찾아봤는데 8081, 8089(Tomcat 돌리고 있는 중)만 사용 중이어서 결국 8080을 왜 못 썼는지는 찾지 못했습니다. ㅠㅠ

다음 시간엔 Ajax로 찾아오겠습니다. 감사합니다 빠잇~!

 

반응형

'Spring > CRUD Project' 카테고리의 다른 글

[CRUD] 파일 업로드 및 조회  (1) 2023.12.04
[CRUD] Ajax : 비동기 통신  (1) 2023.11.30
[CRUD] 검색  (1) 2023.11.28
[CRUD] Delete : 글 삭제하기  (1) 2023.11.27
[CRUD] Update : 글 수정하기  (1) 2023.11.24
댓글