55-1. 공지사항 수정하기
0. 버튼을 누르면 수정하기 /삭제하기 (로그인 시에만)
noticeDetailView.jsp
<div align="center">
<a href="<%= contextPath %>/list.no" class="btn btn-sm btn-info">목록가기</a>
<!-- 작성자만 보이게끔 -->
<!-- 로그인이 되어있고 그리고 현재 로그인한 사용자의 아이디가 게시글 작성자의 아이디와 동일하다면 -->
<% if(loginUser != null && loginUser.getUserId().equals(n.getNoticeWriter())) { %>
<a href="<%= contextPath %>/updateForm.no?nno=<%= n.getNoticeNo() %>" class="btn btn-sm btn-warning">수정하기</a>
<a href="<%= contextPath %>/delete.no?nno=<%= n.getNoticeNo() %>" class="btn btn-sm btn-danger">삭제하기</a>
<% } %>
</div>
1. Controller
NoticeUpdateFormController.java
@WebServlet("/updateForm.no")
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// GET방식
// 2) 값 뽑기
int noticeNo = Integer.parseInt(request.getParameter("nno"));
// 3) 가공
// 4) Service단 메소드 호출
// 상세조회 시 selectNotice() 재활용 => 호출하면 끝
Notice n = new NoticeService().selectNotice(noticeNo);
// n에 글번호, 제목, 내용, 작성자아이디, 작성일
// 5) 응답뷰 지정
request.setAttribute("n", n);
request.getRequestDispatcher("views/notice/noticeUpdateForm.jsp").forward(request, response);
}
2. Service
public int updateNotice(Notice n) {
Connection conn = getConnection();
int result = new NoticeDao().updateNotice(conn, n);
if(result > 0) {
commit(conn);
} else {
rollback(conn);
}
close(conn);
return result;
}
3. DAO
public int updateNotice(Connection conn, Notice n) {
int result = 0;
PreparedStatement pstmt = null;
String sql = prop.getProperty("updateNotice");
try {
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, n.getNoticeTitle());
pstmt.setString(2, n.getNoticeContent());
pstmt.setInt(3, n.getNoticeNo());
result = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(pstmt);
}
return result;
}
4. SQL
<entry key="updateNotice">
UPDATE
NOTICE
SET
NOTICE_TITLE =?,
NOTICE_CONTENT =?
WHERE
NOTICE_NO = ?
AND
STATUS = 'Y'
</entry>
5. View
noticeUpdateForm.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="com.kh.jsp.notice.model.vo.Notice" %>
<%
Notice n = (Notice)request.getAttribute("n");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>공지사항 수정</title>
<style>
#update-form > table{
border : 1px solid white;
}
#update-form input, #update-form textarea{
width : 100%;
}
</style>
</head>
<body>
<%@ include file="../common/menubar.jsp" %>
<div class="outer">
<br>
<h2 align="center">공지사항 수정하기</h2>
<br><br>
<form action="<%= contextPath %>/update.no" method="post" id="update-form">
<input type="hidden" name="nno" value="<%= n.getNoticeNo() %>">
<table align="center">
<tr>
<th width="50">제목</th>
<td width="700">
<input type="text" name="title" required value="<%= n.getNoticeTitle() %>">
</td>
</tr>
<tr>
<th>내용</th>
<td></td>
</tr>
<tr>
<td colspan="2">
<textarea name="content" rows="10" style="resize:none;" required><%= n.getNoticeContent() %></textarea>
</td>
</tr>
</table>
<br><br>
<div align="center">
<button type="submit" class="btn btn-sm btn-primary">수정하기</button>
<button type="button" class="btn btn-sm btn-secondary" onclick="history.back();">뒤로가기</button>
<!-- history.back() 이전페이지로 돌아감(내 브라우저에 있던)-->
</div>
</form>
</div>
</body>
</html>
6. NoticeUpdateController.java
package com.kh.jsp.notice.controller;
import java.io.IOException;
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.kh.jsp.notice.model.service.NoticeService;
import com.kh.jsp.notice.model.vo.Notice;
/**
* Servlet implementation class NoticeUpdateController
*/
@WebServlet("/update.no")
public class NoticeUpdateController extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public NoticeUpdateController() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// POST
// 1) 인코딩
request.setCharacterEncoding("UTF-8");
// 2) 값 뽑기
// 뽑아야하는 값 PK == noticeNo
// 제목 / 내용
int noticeNo = Integer.parseInt(request.getParameter("nno"));
String noticeTitle = request.getParameter("title");
String noticeContent = request.getParameter("content");
// 오류가 나면 null이야? 찍어볼것
// System.out.println(noticeNo);
// System.out.println(noticeTitle);
// System.out.println(noticeContent);
// 3) 가공
Notice n = new Notice();
n.setNoticeNo(noticeNo);
n.setNoticeTitle(noticeTitle);
n.setNoticeContent(noticeContent);
// 4) 서비스 호출
int result = new NoticeService().updateNotice(n);
// 5) 응답화면 지정
if(result > 0 ) { // 성공 => 해당 글 상세보기 페이지로 응답뷰 지정
request.getSession().setAttribute("alertMsg", "추카추카~");
// 1. forward --> 얘는 별로
// 2. Redirect
// http://localhost:8001/jsp/detail.no?nno=공지사항번호
// localhost:8001/jsp
response.sendRedirect(request.getContextPath() + "/detail.no?nno=" + noticeNo);
} else {
request.setAttribute("errorMsg", "공지사항 수정 실패!!");
request.getRequestDispatcher("views/common/errorPage.jsp").forward(request, response);
}
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
55-2. 공지사항 삭제하기
1. view
버튼 누르면 삭제하기~
2. Controller
package com.kh.jsp.notice.controller;
import java.io.IOException;
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.kh.jsp.notice.model.service.NoticeService;
/**
* Servlet implementation class NoticeDeleteController
*/
@WebServlet("/delete.no")
public class NoticeDeleteController extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public NoticeDeleteController() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int noticeNo = Integer.parseInt(request.getParameter("nno"));
int result = new NoticeService().deleteNotice(noticeNo);
if(result > 0) {
request.getSession().setAttribute("alertMsg", "삭제성공~");
response.sendRedirect(request.getContextPath() + "/list.no");
} else {
request.setAttribute("errorMsg", "삭제에 실패하셨습니다.");
request.getRequestDispatcher("views/common/errorPage.jsp").forward(request, response);
}
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
3. Service
public int deleteNotice(int noticeNo) {
Connection conn = getConnection();
int result = new NoticeDao().deleteNotice(conn, noticeNo);
if(result > 0) {
commit(conn);
} else {
rollback(conn);
}
close(conn);
return result;
}
4. DAO
public int deleteNotice(Connection conn, int noticeNo) {
int result = 0;
PreparedStatement pstmt = null;
String sql = prop.getProperty("deleteNotice");
try {
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, noticeNo);
result = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(pstmt);
}
return result;
}
5. SQL
<entry key="deleteNotice">
UPDATE
NOTICE
SET
STATUS = 'N'
WHERE
NOTICE_NO = ?
AND
STATUS = 'Y'
</entry>
55-3. 게시판 시작
#테이블에 컬럼값 INSERT 해놓는 방법
엑셀로 파일 저장

넣을 테이블에 우클릭 후 임포트

파일 경로를 찾아서 파일열기

파일내용에 잘 나오면 다음>다음>다음>완료

예

INSERT문 만들어줌 ㅎㅎ
INSERT문 맨 아래 'commit;' 해주고 스크립트 실행! 끝.
55-4. 일반게시판 목록조회(페이징처리)
1. 메인화면에서 게시판으로 넘어가게 만들기
menubar.jsp
<div class="menu"><a href="<%= contextPath %>/list.bo">일반게시판</a></div>
2. Board VO만들기
테이블보고 잘~ 만들어준다.
boardWriter는 NUMBER형이지만 String형으로 만들어준다.
private int boardNo;
private int boardType;
private int categoryNo;
private String boardTitle;
private String boardContent;
private String boardWriter;
private int count;
private Date createDate;
private String status;
3. BoardListController.java
JSP a태그의 href속성값 = Servlet (@애노테이션)mapping값
a태그로 넘어오면 GET방식!
@WebServlet("/list.bo")
// 불라불라
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// views/board/boardListView.jsp
request.getRequestDispatcher("views/board/boardListView.jsp").forward(request, response);
}
게시글ListView를 보여주자.
4. boardListView.jsp
../은 상위폴더~
화면단은 VS Code에서 작성하자.
5. 다시 지정(page의 첫번째가 나올 수 있게)
menubar.jsp
게시글 번호에 맞게 page를 QueryString으로 값을 넘겨준다.
<div class="menu"><a href="<%= contextPath %>/list.bo?cpage=1">일반게시판</a></div>
6. 한 페이지에 10개의 게시글이 조회될 수 있게 가공해준다.
BoardListController.java
int형/int형은 int형값이 반환된다 => 만약 값이 실수라면 내림한 상태로 나온다.
하나라도 double로 캐스팅해줘야 double형이 나옴.
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 쿼리스트링요청/jsp/list.bo?cpage=1 GET방식 -> 인코딩X
// 2) request값 뽑기
// -- 페이징 처리 --
// 필요한 변수들
int listCount; // 현재 일반게시판의 게시글 총 개수 => BOARD테이블로부터 COUNT(*)활용(STATUS = 'Y')해서 조회
int currentPage; // 현재 페이지(사용자가 요청한 페이지) => request.getParameter("cpage")
int pageLimit; // 페이지 하단에 보여질 페이징바의 최대 개수 => 10개로 고정
int boardLimit; // 한 페이지에 보여질 게시글의 최대 개수 => 10개로 고정
int maxPage; // 가장 마지막 페이지가 몇 번 페이지인지(총 페이지의 개수)
int startPage; // 페이지 하단에 보여질 페이징바의 시작 수
int endPage; // 페이지 하단에 보여질 페이징바의 끝 수
// * listCount : 총 게시글의 수
listCount = new BoardService().selectListCount(); // 107
// * CurrentPage : 현재 페이지(사용자가 요청한 페이지)
currentPage = Integer.parseInt(request.getParameter("cpage")); // 1
// System.out.println(listCount);
// System.out.println(currentPage);
// * pageLimit : 페이징바 최대개수
pageLimit = 10;
// * boardLimit : 한 페이지에 보여질 게시글의 최대 개수
boardLimit = 10;
// * maxPage : 가장 마지막 페이지가 몇 번 페이지인지(총 페이지 개수)
/*
* listCount, boardLimit에 영향을 받음
*
* - 공식 구하기
* 단, boardLimit이 10이라는 가정하에 규칙을 찾아보자
*
* 총 개수(listCount) boardLimit(10개) maxPage(마지막페이지)
* 100개 / 10개 = 10번 페이지
* 107개 / 10개 = 10.7 11페이지
* 111개 / 10개 = 11.1 12페이지
*
* => 나눗셈결과(listCount/boardLimit)를 올림처리를 할 경우
*
* 스텝
* 1. listCount를 double로 변환
* 2. listCount / boardLimit
* 3. Math.ceil() => 결과를 올림처리
*
*/
maxPage = (int)(Math.ceil((double)listCount / boardLimit));
// System.out.println(maxPage);
// * startPage : 페이지 하단에 보여질 페이징바의 시작 수
/*
* pageLimit, currentPage에 영향을 받음
*
* - 공식 구하기
* 단, pageLimit이 10이라는 가정하에 규칙을 구해보자
*
* startPage : 1, 11, 21, 31, 41, ... => n * 10 + 1
*
* 만약에 pageLimit이 5였다??
*
* startPage : 1, 6, 11, 16, ... => n * 5 + 1
*
* 즉, startPage = n * pageLimit + 1;
*
* currentPage startPage
* 1 1
* 5 1
* 10 1
* 13 11
* 20 11
* 29 21
*
* => 1 ~ 10 : n * 10 + 1 => n == 0
* => 11 ~ 20 : n * 10 + 1 => n == 1
* => 21 ~ 30 : n * 10 + 1 => n == 2
*
* 1 ~ 10 / 10 => 0 ~ 1
* 11 ~ 20 / 10 => 1 ~ 2
* 21 ~ 30 / 10 => 2 ~ 3
*
* 0 ~ 9 / 10 => 0
* 10 ~ 19 / 10 => 1
* 20 ~ 29 / 10 => 2
*
* n = (currentPage - 1) / pageLimit
*
* startPage = (currentPage - 1) / pageLimit * pageLimit + 1;
*/
startPage = (currentPage - 1) / pageLimit * pageLimit + 1;
// * endPage : 페이지 하단에 보여질 페이징바의 끝 수
/*
* startPage, pageLimit에 영향을 받음(단, maxPage도 마지막 페이징 바에 대해 영향을 끼침)
*
* - 공식 구하기
* 단, pageLimit이 10이라는 가정하에
*
* startPage : 1 => endPage : 10
* startPage : 11 => endPage : 20
* startPage : 21 => endPage : 30
*
* => endPage = startPage + pageLimit - 1;
*/
endPage = startPage + pageLimit - 1;
// startPage가 21이라서 endPage에는 30이 들어갔는데
// maxPage가 23이라면?
// endPage값을 maxPage값으로 변경
if(endPage > maxPage) {
endPage = maxPage;
}
// 여기까지 총 7개의 변수를 만들었음!
// 3) VO로 가공
PageInfo pi = new PageInfo(listCount, currentPage, pageLimit, boardLimit, maxPage, startPage, endPage);
7. 따로 변수들을 가지고 있는 PageInfo 객체 생성
PageInfo.java (VO)
public class PageInfo {
private int listCount; // 현재 일반게시판의 게시글 총 개수 => BOARD테이블로부터 COUNT(*)활용(STATUS = 'Y')해서 조회
private int currentPage; // 현재 페이지(사용자가 요청한 페이지) => request.getParameter("cpage")
private int pageLimit; // 페이지 하단에 보여질 페이징바의 최대 개수 => 10개로 고정
private int boardLimit; // 한 페이지에 보여질 게시글의 최대 개수 => 10개로 고정
private int maxPage; // 가장 마지막 페이지가 몇 번 페이지인지(총 페이지의 개수)
private int startPage; // 페이지 하단에 보여질 페이징바의 시작 수
private int endPage; // 페이지 하단에 보여질 페이징바의 끝 수
8. BoardListController.java
Service로 전달 후 결과값 화면JSP로 돌려주기
// 4) Service로 가자~
ArrayList<Board> list = new BoardService().selectList(pi);
request.setAttribute("list", list); // 우리가 실제로 조회한 페이지에 보여질 10개의 게시글
request.setAttribute("pi", pi);
// views/board/boardListView.jsp
request.getRequestDispatcher("views/board/boardListView.jsp").forward(request, response);
}
9. BoardService.java
현재 일반게시판의 게시글 총 개수 구하기 및 게시글 목록 가져오기
package com.kh.jsp.board.model.sevice;
import static com.kh.jsp.common.JDBCTemplate.close;
import static com.kh.jsp.common.JDBCTemplate.getConnection;
import java.sql.Connection;
import java.util.ArrayList;
import com.kh.jsp.board.model.dao.BoardDao;
import com.kh.jsp.board.model.vo.Board;
import com.kh.jsp.board.model.vo.PageInfo;
public class BoardService {
public int selectListCount() {
Connection conn = getConnection();
int listCount = new BoardDao().selectListCount(conn);
close(conn);
return listCount;
}
public ArrayList<Board> selectList(PageInfo pi) {
Connection conn = getConnection();
ArrayList<Board> list = new BoardDao().selectList(conn, pi);
close(conn);
return list;
}
}
10. BoardDao.java
현재 일반게시판의 게시글 총 개수 구하기 및 게시글 목록 가져오기
package com.kh.jsp.board.model.dao;
import static com.kh.jsp.common.JDBCTemplate.close;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Properties;
import com.kh.jsp.board.model.vo.Board;
import com.kh.jsp.board.model.vo.PageInfo;
public class BoardDao {
private Properties prop = new Properties();
public BoardDao() {
String filePath = BoardDao.class.getResource("/sql/board/board-mapper.xml").getPath();
try {
prop.loadFromXML(new FileInputStream(filePath));
} catch (IOException e) {
e.printStackTrace();
}
}
public int selectListCount(Connection conn) {
int listCount = 0;
PreparedStatement pstmt = null;
ResultSet rset = null;
String sql = prop.getProperty("selectListCount");
try {
pstmt = conn.prepareStatement(sql);
rset = pstmt.executeQuery();
if(rset.next()) {
listCount = rset.getInt("COUNT(*)");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(rset);
close(pstmt);
}
return listCount;
// t.w.r 약어... 찾아보기?
}
public ArrayList<Board> selectList(Connection conn, PageInfo pi) {
ArrayList<Board> list = new ArrayList();
PreparedStatement pstmt = null;
ResultSet rset = null;
String sql = prop.getProperty("selectList");
try {
pstmt = conn.prepareStatement(sql);
// 인라인 뷰 활용
// 1) ORDER BY절 순서가 가장 마지막인데 정렬이 끝난 상태가 필요함!
// 일단 정렬해주는 SELECT문 만듦 => 서브쿼리
// 2) 서브쿼리 FROM절에 넣음 + ROWNUM
/*
* boardLimit이 10이라는 가정하에
* currentPage == 1 => 시작값 1, 끝값 10
* currentPage == 2 => 시작값 11, 끝값 20
* currentPage == 3 => 시작값 21, 끝값 30
*
* 시작값 = (currentPage -1) * boardLimit + 1
* 끝값 = 시작값 + boardLimit - 1;
*/
int startRow = (pi.getCurrentPage() -1) * pi.getBoardLimit() + 1;
int endRow = startRow + pi.getBoardLimit() - 1;
pstmt.setInt(1, startRow);
pstmt.setInt(2, endRow);
rset = pstmt.executeQuery();
while(rset.next()) {
Board b = new Board();
b.setBoardNo(rset.getInt("BOARD_NO"));
b.setBoardTitle(rset.getString("BOARD_TITLE"));
b.setBoardWriter(rset.getString("USER_ID"));
b.setCount(rset.getInt("COUNT"));
b.setCreateDate(rset.getDate("CREATE_DATE"));
b.setCategory(rset.getString("CATEGORY_NAME"));
list.add(b);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(rset);
close(pstmt);
}
return list;
}
}
11. SQL
board-mapper.xml
현재 일반게시판의 게시글 총 개수 구하기 및 게시글 목록 가져오기
오라클 자체적 문제...
ROWNUM BETWEEN 1 AND 10; 은 괜찮은데
ROWNUM BETWEEN 11 AND 20; 처럼 되는 건 못 씀..
ROWNUM은 1부터만 시작할 수 있음! => 별칭(RNUM)을 붙여서 사용한다.
=> 조회결과가 SELECT
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>게시판 관련 SQL문</comment>
<entry key="selectListCount">
SELECT
COUNT(*)
FROM
BOARD
WHERE
STATUS = 'Y'
AND
BOARD_TYPE = 1
</entry>
<entry key="selectList">
SELECT *
FROM (SELECT ROWNUM RNUM, A.*
FROM (SELECT
BOARD_NO,
CATEGORY_NAME,
BOARD_TITLE,
USER_ID,
COUNT,
CREATE_DATE
FROM
BOARD
JOIN
CATEGORY USING(CATEGORY_NO)
JOIN
MEMBER ON(BOARD_WRITER = USER_NO)
WHERE
BOARD.STATUS = 'Y'
AND
BOARD_TYPE = 1
ORDER
BY CREATE_DATE DESC) A)
WHERE RNUM BETWEEN ? AND ?
</entry>
</properties>
12. BoardListController.java
게시글 조회 시
JOIN하여 CATEGORY를 조회해야 하는데
현재 BOARD에는 CATEGORY_NO밖에 없다.
BAORD VO에 CATEGORY를 추가해준다.
private String category;
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
13. BoardListView.jsp
Controller에서 넘어온 값들 가져와서 화면에 띄어주기
http://localhost:8001/jsp/list.bo?cpage=1
cpage=? 값에 번호를 바꿔 넣어주면 목록이 바뀐다!
+ 페이징처리까지
<%@ page import="java.util.ArrayList, com.kh.jsp.board.model.vo.Board, com.kh.jsp.board.model.vo.PageInfo" %>
<%
ArrayList<Board> list = (ArrayList<Board>)request.getAttribute("list");
PageInfo pi = (PageInfo)request.getAttribute("pi");
// 페이징바 만들 때 필요한 변수 미리 세팅
int currentPage = pi.getCurrentPage();
int startPage = pi.getStartPage();
int endPage = pi.getEndPage();
int maxPage = pi.getMaxPage();
%>
-------------------------------------------------------------------------------------
<body>
<%@ include file="../common/menubar.jsp" %>
<div class="outer">
<br>
<h2 align="center">일반게시판</h2>
<br>
<table class="list-area" align="center">
<thead>
<tr>
<th width="100">글번호</th>
<th width="100">카테고리</th>
<th width="300">제목</th>
<th width="100">작성자</th>
<th width="50">조회수</th>
<th width="100">작성일</th>
</tr>
</thead>
<tbody>
<% if(list.isEmpty()) { %>
<tr>
<td colspan="6">조회된 게시글이 없습니다.</td>
</tr>
<% } else { %>
<% for(Board b : list) { %>
<tr>
<td><%= b.getBoardNo() %></td>
<td><%= b.getCategory() %></td>
<td><%= b.getBoardTitle() %></td>
<td><%= b.getBoardWriter() %></td>
<td><%= b.getCount() %></td>
<td><%= b.getCreateDate() %></td>
</tr>
<% } %>
<% } %>
</tbody>
</table>
<div class="paging-area" align="center">
<!-- <를 누르면 이전 페이지로 이동 / 첫페이지일 경우 <버튼 자체가 안보임 -->
<% if(currentPage != 1) { %>
<button onclick="location.href='<%=contextPath%>/list.bo?cpage=<%=currentPage - 1%>'" class="btn btn-sm btn-info"><</button>
<% } %>
<% for(int i = startPage; i <= endPage; i++) { %>
<% if(currentPage != i) { %>
<button onclick="location.href='<%=contextPath%>/list.bo?cpage=<%=i%>'" class="btn btn-sm btn-info"><%= i %></button>
<% } else { %>
<button disabled class="btn btn-sm btn-info"><%= i %></button>
<% } %>
<% } %>
<!-- >를 누르면 다음 페이지로 이동 / 마지막페이지일 경우 >버튼 자체가 안보임 -->
<% if(currentPage != maxPage) { %>
<button onclick="location.href='<%=contextPath %>/list.bo?cpage=<%= currentPage + 1 %>'" class="btn btn-sm btn-info">></button>
<% } %>
</div>
<br>
<br>
</div>
</body>
페이징 처리는 외우고 써도됨