본문 바로가기
MyBatis

[MyBatis] select (3) 여러행 + 페이징 처리 (RowBounds 적용)

by moca7 2024. 10. 11.

 

 

 

ㅁ 게시글이 몇개일지 모르기 때문에 웬만해선 앞으론 다 페이징 처리를 한다. 

- MyBatis는 페이징 처리를 위한 객체(RowBounds)를 제공해서 쿼리문이 간결해진다.

 

 

 

ㅁ main.jsp

 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

    <h3>1. insert연습 (공지사항 등록)</h3>
    <a href="${ contextPath }/regist.no">공지사항 등록페이지로 이동</a>

    <h3>2. select연습 - 한 행 (공지사항 상세조회)</h3>
    <form action="${ contextPath }/detail.no">
        검색할 글번호 : <input type="text" name="noticeNo">
        <button type="submit">검색</button>
    </form>

    <h3>3. select연습 - 여러행 (공지사항 전체목록조회)</h3>
    <a href="${ contextPath }/list.no">공지사항 전체 목록 페이지로 이동</a>


    <h4>4. select연습 - 여러행 + 페이징 처리 - RowBounds 적용</h4>
    <a href="${ contextPath }/list.no?page=1">공지사항 목록 페이지(페이징처리)로 이동</a>


    <h5>5. select연습 + update연습 (회원정보 상세조회 + 회원정보 변경) - TypeHandler 적용</h5>
   
    <h6>6. 동적쿼리 연습 (회원 검색)</h6>

</body>
</html>
 

 

 

- 페이징 처리를 적용한 여러행 조회 요청을 위해 새로운 서블릿을 만들어도 되지만 이전걸 재사용한다.

아까는 요청시 전달값이 없었지만 이번에는 요청시 전달값을 전달한다.

 

- 넘어가는 데이터가 없으면 전체 목록페이지가 보이게 하고, 넘어가는 데이터가 있으면 페이징 처리를 한다.

 

 

 

 

 

ㅁ com.br.mybatis.notice.controller의 NoticeListController를 수정한다.

 


package com.br.mybatis.notice.controller;

import java.io.IOException;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.br.mybatis.notice.dto.NoticeDto;
import com.br.mybatis.notice.service.NoticeServiceImpl;

/**
 * Servlet implementation class NoticeListController
 */
@WebServlet("/list.no")
public class NoticeListController extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public NoticeListController() {
        super();
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        if(request.getParameter("page") == null) {

            // 페이징 처리 X
            List<NoticeDto> list = new NoticeServiceImpl().selectList();

            request.setAttribute("list", list);
            request.getRequestDispatcher("/WEB-INF/views/notice/noticeList.jsp").forward(request, response);

        }else {

            // 페이징 처리 O
            int currentPage = Integer.parseInt(request.getParameter("page"));
            int listCount = new NoticeServiceImpl().selectListCount();

            PageInfoDto pi = PagingUtil.getPageInfoDto(listCount, currentPage, 5, 5);
            List<NoticeDto> list = new NoticeServiceImpl().selectList(pi);

        }


       
       
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}

 

 

 

 


ㅁ com.br.mybatis.notice.service의 NoticeServiceImpl에 selectListCount 메소드를 만든다.

 

 
package com.br.mybatis.notice.service;

import static com.br.mybatis.common.template.Template.getSqlSession;

import java.util.List;

import org.apache.ibatis.session.SqlSession;

import com.br.mybatis.common.dto.PageInfoDto;
import com.br.mybatis.notice.dao.NoticeDao;
import com.br.mybatis.notice.dto.NoticeDto;

public class NoticeServiceImpl implements NoticeService {
   
   
    private NoticeDao noticeDao = new NoticeDao();
   


    @Override
    public int insertNotice(NoticeDto n) {
       
        SqlSession sqlSession = /*Template.*/getSqlSession();
        int result = noticeDao.insertNotice(sqlSession, n);
       
        if(result > 0) {
            sqlSession.commit();
        }
       
        sqlSession.close();
        return result;
    }

   
   
    @Override
    public NoticeDto selectNotice(int noticeNo) {
       
        SqlSession sqlSession = getSqlSession();
        NoticeDto n = noticeDao.selectNotice(sqlSession, noticeNo);
       
        sqlSession.close();
        return n;
    }

   
    @Override
    public List<NoticeDto> selectList() {
       
        SqlSession sqlSession = getSqlSession();
        List<NoticeDto> list = noticeDao.selectList(sqlSession);
       
        sqlSession.close();
        return list;
    }

   
    @Override
    public int selectListCount() {
       
        SqlSession sqlSession = getSqlSession();
        int listCount = noticeDao.selectListCount(sqlSession);

        sqlSession.close();
        return listCount;
    }

   
    @Override
    public List<NoticeDto> selectList(PageInfoDto pi) {
        return null;
    }
   

}
 

 

 

- 게시글이 총 몇개인지는 db로부터 조회 한다.

 

 

 

 


ㅁ com.br.mybatis.notice.dao의 NoticeDao에 selectListCount 메소드를 만든다.

 

 
package com.br.mybatis.notice.dao;

import java.util.List;

import org.apache.ibatis.session.SqlSession;

import com.br.mybatis.notice.dto.NoticeDto;

public class NoticeDao {

    public int insertNotice(SqlSession sqlSession, NoticeDto n) {
       
        return sqlSession.insert("noticeMapper.insertNotice", n);
    }
   
    public int updateNotice(SqlSession sqlSession, NoticeDto n) {
       
        return sqlSession.update("noticeMapper.updateNotice", n);
       
    }
   
    public int deleteNotice(SqlSession sqlSession, int noticeNo) {
       
        return sqlSession.delete("noticeMapper.deleteNotice", noticeNo);
    }

    public NoticeDto selectNotice(SqlSession sqlSession, int noticeNo) {
       
        return sqlSession.selectOne("noticeMapper.selectNotice", noticeNo);
    }

    public List<NoticeDto> selectList(SqlSession sqlSession) {
       
        return sqlSession.selectList("noticeMapper.selectList");
    }

    public int selectListCount(SqlSession sqlSession) {
       
        return sqlSession.selectOne("noticeMapper.selectListCount");
    }

}
 

 

- 게시글이 총 몇개인지는 db로부터 조회 한다.

 

 

 

 

ㅁ 쿼리

 


    <select id="selectListCount" resultType="_int">
        select  
                      count(*)
           from   notice  
         where   status = 'Y'    
    </select>
 

 

 

- resultType으로 selectOne() 메소드의 결과가 반환된다. 그 결과를 dao에서 또 service로 return하고 있다.

 

 

 

 

 

 

ㅁ com.br.mybatis.common.dto 패키지에 PageInfoDto 클래스를 처음에 만들었었다.

 

 
package com.br.mybatis.common.dto;

public class PageInfoDto {

    private int listCount;
    private int currentPage;
    private int pageLimit;
    private int boardLimit;
    private int maxPage;
    private int startPage;
    private int endPage;
   
   
   
    public PageInfoDto() {
        super();
    }



    public PageInfoDto(int listCount, int currentPage, int pageLimit, int boardLimit, int maxPage, int startPage,
            int endPage) {
        super();
        this.listCount = listCount;
        this.currentPage = currentPage;
        this.pageLimit = pageLimit;
        this.boardLimit = boardLimit;
        this.maxPage = maxPage;
        this.startPage = startPage;
        this.endPage = endPage;
    }


    public int getListCount() {
        return listCount;
    }


    public void setListCount(int listCount) {
        this.listCount = listCount;
    }


    public int getCurrentPage() {
        return currentPage;
    }



    public void setCurrentPage(int currentPage) {
        this.currentPage = currentPage;
    }


    public int getPageLimit() {
        return pageLimit;
    }


    public void setPageLimit(int pageLimit) {
        this.pageLimit = pageLimit;
    }



    public int getBoardLimit() {
        return boardLimit;
    }


    public void setBoardLimit(int boardLimit) {
        this.boardLimit = boardLimit;
    }


    public int getMaxPage() {
        return maxPage;
    }


    public void setMaxPage(int maxPage) {
        this.maxPage = maxPage;
    }


    public int getStartPage() {
        return startPage;
    }


    public void setStartPage(int startPage) {
        this.startPage = startPage;
    }


    public int getEndPage() {
        return endPage;
    }


    public void setEndPage(int endPage) {
        this.endPage = endPage;
    }


    @Override
    public String toString() {
        return "PageInfoDto [listCount=" + listCount + ", currentPage=" + currentPage + ", pageLimit=" + pageLimit
                + ", boardLimit=" + boardLimit + ", maxPage=" + maxPage + ", startPage=" + startPage + ", endPage="
                + endPage + "]";
    }
   
   
   
}
 

 

 

 

 

ㅁ com.br.mybatis.common.utils 패키지에 PagingUtil 클래스를 처음에 만들었었다.

 

 
package com.br.mybatis.common.utils;

import com.br.mybatis.common.dto.PageInfoDto;

public class PagingUtil {

    // listCount, currentPage, pageLimit, boardLimit을 전달받아
    // maxPage, startPage, endPage를 구해서
    // PageInfoDto에 담아서 반환한다.
   
    public static PageInfoDto getPageInfoDto(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;
        }
       
        return new PageInfoDto(listCount, currentPage, pageLimit, boardLimit, maxPage, startPage, endPage);
       
    }
   
   
}
 

 

 

- PagingUtil 클래스의 getPageInfoDto 메소드를 호출하면서

listCount, currentPage, pageLimit, boardLimit을 전달하면

maxPage, startPage, endPage를 구하고 PageInfoDto 객체에 담으면서 생성해서 리턴한다.

 

 

 

 

 

ㅁ 컨트롤러로 돌아온다.

 

 
package com.br.mybatis.notice.controller;

import java.io.IOException;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.br.mybatis.notice.dto.NoticeDto;
import com.br.mybatis.notice.service.NoticeServiceImpl;

/**
 * Servlet implementation class NoticeListController
 */
@WebServlet("/list.no")
public class NoticeListController extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public NoticeListController() {
        super();
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        if(request.getParameter("page") == null) {

            // 페이징 처리 X
            List<NoticeDto> list = new NoticeServiceImpl().selectList();

            request.setAttribute("list", list);
            request.getRequestDispatcher("/WEB-INF/views/notice/noticeList.jsp").forward(request, response);

        }else {

            // 페이징 처리 O
            int currentPage = Integer.parseInt(request.getParameter("page"));
            int listCount = new NoticeServiceImpl().selectListCount();

            PageInfoDto pi = PagingUtil.getPageInfoDto(listCount, currentPage, 5, 5);
            List<NoticeDto> list = new NoticeServiceImpl().selectList(pi);

        }


       
       
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}
 

 

 

- 아직 컨트롤러 클래스의 완성 형태는 아니다.

- PaginUtil 클래스의 getPageInfoDto 메소드 호출시 listCount, currentPage, pageLimit, boardLimit을 넘기면 

PageInfoDto 객체가 돌아온다.

- 페이징 바 제작을 위한 PageInfoDto 객체도 얻었으니 사용자에게 보여줄 일부 게시글 목록만을 조회할 서비스 메소드를 호출한다.

 

 

 

 

ㅁ NoticeServiceImpl 클래스에 selectList 메소드 생성

 

 
package com.br.mybatis.notice.service;

import static com.br.mybatis.common.template.Template.getSqlSession;

import java.util.List;

import org.apache.ibatis.session.SqlSession;

import com.br.mybatis.common.dto.PageInfoDto;
import com.br.mybatis.notice.dao.NoticeDao;
import com.br.mybatis.notice.dto.NoticeDto;

public class NoticeServiceImpl implements NoticeService {
   
   
    private NoticeDao noticeDao = new NoticeDao();
   


    @Override
    public int insertNotice(NoticeDto n) {
       
        SqlSession sqlSession = /*Template.*/getSqlSession();
       
        int result = noticeDao.insertNotice(sqlSession, n);
       
        if(result > 0) {
            sqlSession.commit();
        }
       
        sqlSession.close();
       
        return result;
    }

   
   
    @Override
    public NoticeDto selectNotice(int noticeNo) {
       
        SqlSession sqlSession = getSqlSession();
        NoticeDto n = noticeDao.selectNotice(sqlSession, noticeNo);
       
        sqlSession.close();
        return n;
    }

   
    @Override
    public List<NoticeDto> selectList() {
       
        SqlSession sqlSession = getSqlSession();
        List<NoticeDto> list = noticeDao.selectList(sqlSession);
       
        sqlSession.close();
        return list;
    }

   
    @Override
    public int selectListCount() {
       
        SqlSession sqlSession = getSqlSession();
        int listCount = noticeDao.selectListCount(sqlSession);

        sqlSession.close();
        return listCount;
    }

   
    @Override
    public List<NoticeDto> selectList(PageInfoDto pi) {
        SqlSession sqlSession = getSqlSession();
        List<NoticeDto> list = noticeDao.selectList(sqlSession, pi);

        sqlSession.close();
        return list;
    }
   

}

 

 

 

 

ㅁ NoticeDao 클래스에 selectList 메소드 생성

 

 
package com.br.mybatis.notice.dao;

import java.util.List;

import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;

import com.br.mybatis.common.dto.PageInfoDto;
import com.br.mybatis.notice.dto.NoticeDto;

public class NoticeDao {

    public int insertNotice(SqlSession sqlSession, NoticeDto n) {
       
        return sqlSession.insert("noticeMapper.insertNotice", n);
    }
   
    public int updateNotice(SqlSession sqlSession, NoticeDto n) {
       
        return sqlSession.update("noticeMapper.updateNotice", n);
       
    }
   
    public int deleteNotice(SqlSession sqlSession, int noticeNo) {
       
        return sqlSession.delete("noticeMapper.deleteNotice", noticeNo);
    }

    public NoticeDto selectNotice(SqlSession sqlSession, int noticeNo) {
       
        return sqlSession.selectOne("noticeMapper.selectNotice", noticeNo);
    }

    public List<NoticeDto> selectList(SqlSession sqlSession) {
       
        return sqlSession.selectList("noticeMapper.selectList");
    }
 

    public int selectListCount(SqlSession sqlSession) {
       
        return sqlSession.selectOne("noticeMapper.selectListCount");
    }

    public List<NoticeDto> selectList(SqlSession sqlSession, PageInfoDto pi) {

        // return sqlSession.selectList("noticeMapper.selectList"); 이렇게만 쓰면 전체 조회되어버린다.

        int limit = pi.getBoardLimit();
        int offset = (pi.getCurrentPage() - 1) * limit;

        RowBounds rowBounds = new RowBounds(offset, limit);

        return sqlSession.selectList("noticeMapper.selectList", null , rowBounds);
        // 완성형태의 쿼리문이라 무언가 전달할 것이 없다.
        // 완성시킬 값이 있다면 2번째 자리에 쓰면 된다.
    }

}

 

 

- 전체 데이터 조회가 아닌, 사용자가 요청한 페이지에 필요한 일부 데이터만 조회한다.

return sqlSession.selectList("noticeMapper.selectList"); 이렇게만 쓰면 전체 조회되어버린다.

 

- 기존에 만들어놓은 selectList 쿼리를 또 쓴다.

마이바티스를 사용하면 인라인뷰로 쿼리를 복잡하게 작성할 필요가 없다.

전체 게시글 중 일부 게시글을 조회하는 쿼리문이 매우 간결하다.

-  RowBounds 객체를 제시해서 매개변수 3개짜리 메소드를 사용하면 된다.

 

 

 

 

 

 

※ RowBounds

- 마이바티스에서 페이징 처리를 위해 제공하는 객체

 

- 전체 게시글을 조회하는 쿼리문을 사용해서 우선은 전체 조회를 해오고,

그 중에 몇 개의 게시글을 건너뛰고 몇 개의 게시글만 반환하게끔 설정할 수 있다.

 

 

 

RowBounds rowBounds = new RowBounds(offset, limit);

 

- RowBounds 객체를 생성할 때 2개의 매개변수를 제시해야 한다.

- offset : 건너뛸 갯수

- limit : 조회할 갯수 (건너 뛰고 나서 몇개를 조회할 건지)

 

 

 

 

 

                                                                               offset        limit

currentPage : 1        1번게시글~5번게시글               0             5

currentPage : 2        6번게시글~10번게시글             5             5

currentPage : 3       11번게시글~15번게시글           10            5

 

 

 

 

 

 

 

 

 

 

- DB에서 쿼리문으로 전체 게시글을 조회했다.

- 1번 페이지라면 0개의 게시글을 건너뛰고 5개의 게시글을 조회해야 한다.

- 2번 페이지라면 5개의 게시글을 건너뛰고 6번째 데이터부터 5개의 게시글을 조회해야 한다.

 

- 몇번 페이지냐에 따라서 몇개의 게시글을 건너뛸지는 직접 결정해야 한다.

- 전체 게시글을 조회하는 sql문을 실행할 때 RowBounds 객체를 넘기면 몇개의 게시글을 건너 뛰고 몇개의 게시글을 조회할지 세팅된다.

 

 

 

 

 

 

ㅁ 다시 dao

 

 

 

- selectList 메소드는 3종류가 존재한다.

 

 

 

 
package com.br.mybatis.notice.dao;

import java.util.List;

import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;

import com.br.mybatis.common.dto.PageInfoDto;
import com.br.mybatis.notice.dto.NoticeDto;

public class NoticeDao {

    public int insertNotice(SqlSession sqlSession, NoticeDto n) {
       
        return sqlSession.insert("noticeMapper.insertNotice", n);
    }
   
    public int updateNotice(SqlSession sqlSession, NoticeDto n) {
       
        return sqlSession.update("noticeMapper.updateNotice", n);
       
    }
   
    public int deleteNotice(SqlSession sqlSession, int noticeNo) {
       
        return sqlSession.delete("noticeMapper.deleteNotice", noticeNo);
    }

    public NoticeDto selectNotice(SqlSession sqlSession, int noticeNo) {
       
        return sqlSession.selectOne("noticeMapper.selectNotice", noticeNo);
    }

    public List<NoticeDto> selectList(SqlSession sqlSession) {
       
        return sqlSession.selectList("noticeMapper.selectList");
    }

    public int selectListCount(SqlSession sqlSession) {
       
        return sqlSession.selectOne("noticeMapper.selectListCount");
    }
 

    public List<NoticeDto> selectList(SqlSession sqlSession, PageInfoDto pi) {

        // return sqlSession.selectList("noticeMapper.selectList"); 이렇게만 쓰면 전체 조회되어버린다.
 

        int limit = pi.getBoardLimit();
        int offset = (pi.getCurrentPage() - 1) * limit;

        RowBounds rowBounds = new RowBounds(offset, limit);

        return sqlSession.selectList("noticeMapper.selectList", null , rowBounds);
 
    }

}
 

 

 

- 완성형태의 쿼리문이라 무언가 전달할 것이 없으면 null을 쓴다.

완성시킬 값이 있다면 2번째 자리에 쓰면 된다.

 

 

 

 

 

ㅁ 컨트롤러로 돌아와서 완성한다

 

 
package com.br.mybatis.notice.controller;

import java.io.IOException;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.br.mybatis.common.dto.PageInfoDto;
import com.br.mybatis.common.utils.PagingUtil;
import com.br.mybatis.notice.dto.NoticeDto;
import com.br.mybatis.notice.service.NoticeServiceImpl;

/**
 * Servlet implementation class NoticeListController
 */
@WebServlet("/list.no")
public class NoticeListController extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public NoticeListController() {
        super();
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        if(request.getParameter("page") == null) {

            // 페이징 처리 X
            List<NoticeDto> list = new NoticeServiceImpl().selectList();

            request.setAttribute("list", list);
            request.getRequestDispatcher("/WEB-INF/views/notice/noticeList.jsp").forward(request, response);

        }else {

            // 페이징 처리 O
            int currentPage = Integer.parseInt(request.getParameter("page"));
            int listCount = new NoticeServiceImpl().selectListCount();

            PageInfoDto pi = PagingUtil.getPageInfoDto(listCount, currentPage, 5, 5);
            List<NoticeDto> list = new NoticeServiceImpl().selectList(pi);

            request.setAttribute("pi", pi);
            request.setAttribute("list", list);

            request.getRequestDispatcher("/WEB-INF/views/notice/noticePagingList.jsp").forward(request, response);

        }


       
       
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}
 

 

 

 

 

 

ㅁ WEB-INF/views/notice/noticePagingList.jsp 생성

 

 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

    <h3>공지사항 목록 (페이징 o)</h3>
   
    <c:choose>
        <c:when test="${ empty list }">
            존재하는 공지사항이 없습니다
        </c:when>
       
        <c:otherwise>
            <table border="1">
                <thead>
                    <tr>
                        <th>글번호</th>
                        <th>글제목</th>
                        <th>작성자</th>
                        <th>작성일</th>
                    </tr>
                </thead>
                <tbody>
                    <c:forEach var="n" items="${ list }">
                        <tr>
                            <td>${ n.noticeNo }</td>
                            <td>${ n.noticeTitle }</td>
                            <td>${ n.noticeWriter }</td>
                            <td>${ n.registDate }</td>
                        </tr>
                    </c:forEach>
                </tbody>
            </table>
       
       
       
            <!-- 페이징 바 제작 -->  
            <div>
               
                <c:if test="${ pi.currentPage ne 1 }">
                    <a href="${ contextPath }/list.no?page=${ pi.currentPage - 1 }">이전</a>
                </c:if>
               
               
                <c:forEach var="p" begin="${ pi.startPage }" end="${ pi.endPage }">
                    <a href="${ contextPath }/list.no?page=${ p }">${ p }</a>
                </c:forEach>
               
               
                <c:if test="${ pi.currentPage ne pi.maxPage }">
                    <a href="${ contextPath }/list.no?page=${ pi.currentPage + 1 }">다음</a>
                </c:if>
               
            </div>
       
       
        </c:otherwise>
    </c:choose>

</body>
</html>