Servlet/JSP - 공지사항삭제, 일반게시판목록조회, 일반게시글 작성1
1. 공지사항 삭제
<view/noticeDetailView>
<a class="btn btn-sm btn-danger" href="<%=contextPath%>/deleteNotice.no?nno=<%=n.getNoticeNo()%>">삭제</a>
get방식으로 noticeNo을 함께 보내주기
<com.kh.notice.controller>NoticeDeleteNotice
int noticeNo = Integer.parseInt(request.getParameter("nno"));
int result = new NoticeService().deleteNotice(noticeNo);
if(result>0) {
response.sendRedirect(request.getContextPath()+"/list.no");
}else {
request.setAttribute("errorMsg", "공지사항 삭제에 실패하였습니다.");
request.getRequestDispatcher("views/common/errorPage.jsp").forward(request, response);
}
성공했을 경우, NoticeList목록으로 돌아가기 => response.sendRedirect()방식을 이용해 url을 지정해주기
실패했을 경우 alert창을 띄워준 뒤 에러페이지를 보내주기
<com.kh.notice.model.service>NoticeService
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;
}
<com.kh.notice.model.dao>NoticeDao
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;
}
<notice-mapper>"deleteNotice
<entry key="deleteNotice">
UPDATE
NOTICE
SET
STATUS = 'N'
WHERE
NOTICE_NO = ?
AND
STATUS = 'Y'
</entry>
2. 일반게시판 목록 조회
일반게시판의 특징
- 첨부파일
- 페이징처리
<menubar.jsp>
<div class="menu"><a href="<%= contextPath%>/list.bo?cpage=1">일반게시판</a></div>
경로를 .list.bo라고 지정해준 뒤 get방식으로 현재 사용자가 몇 번 째 페이지인지도 함께 보내주기(기본값=1)
<com.kh.board.controlloer>BoardListController
1) 인코딩 => GET방식 쿼리스트링으로 요청 /jsp/list.bo?cpage=1
2) request에서 값뽑기
3) 가공처리하기
페이징 처리
1. 필요한 변수들
1) listCount : 현재 일반 게시판의 게시글 총 개수 => BOARD로부터 조회 COUNT(*)활용(STATUS='Y')
2) currentPage : 현재 페이지 (즉, 사용자가 요청한 페이지) => request.getParameter("cpage")
3) pageLimit : 페이지 하단에 보여질 페이징 바의 최대 개수 (10개로 고정)
4) boardLimit : 한 페이지에 보여질 게시글의 최대 개수 => 10개로 고정
5) maxPage : 가장 마지막 페이지가 몇 번인지(총 페이지 개수)
6) startPage : 페이지 하단에 보여질 페이징바의 시작 수
7) endPage : 페이지 하단에 보여질 페이징바의 끝 수
int listCount;
int currentPage;
int pageLimit;
int boardLimit;
int maxPage;
int startPage;
int endPage;
* 게시글은 언제나 변할 수 있기 때문에 고정된 값이 아닌 변수로 지정해주어야 함
1) listCount :총 게시글 수
listCount = new BoardService().selectListCount();//107
2) currentPage : 현재페이지 (==사용자가 요청한 페이지)
currentPage = Integer.parseInt(request.getParameter("cpage"));//1
- get으로 보내준 방식이니까 getParameter로 뽑아오기
3) pageLimit : 페이징바의 최대 개수
pageLimit = 10;
- 개발자가 임의로 지정한 값
4) boardLimit : 한 페이지에 보여질 게시글의 최대 개수
boardLimit = 10;
- 개발자가 임의로 지정한 값
5) maxPage : 가장 마지막 페이지가 몇 번 페이지인지(총 페이지 개수) 한페이지에 10개 => 11개의 페이지
==> boardLimit, listCount에 영향을 받음
- 공식 구하기
단, boardLimit이 10이라는 가정 하에 규칙을 정해보자
총 개수 (listCount) | 연산자 | boardLimit(10개) | 연산 결과 | maxPage(마지막페이지) |
100개 | / | 10개 | = 10.0 | 10번페이지 |
102개 | / | 10개 | = 10.2 | 11번페이지 |
107개 | / | 10개 | = 10.7 | 11번페이지 |
110개 | / | 10개 | = 11.0 | 11번페이지 |
111개 | / | 10개 | = 11.1 | 12번페이지 |
=> 나눗셈 결과 (listCount/boardLimit)를 올림처리를 할 경우 maxPage가 된다.
< STEP >
1) listCount를 double로 형변환
2) listCount / boardLimit
3) Math.ceil() => 결과를 올림 처리
4) 결과값을 int로 형변환
maxPage = (int)Math.ceil((double)listCount/boardLimit);
6) 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 | 28 - 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 | 0/1 |
11~20 | / | 10=>1 | 1/2 |
21~30 | / | 10=>2 | 2/3 |
=> 1씩 모자름
1 ~ 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;
7) 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가 11이어서 endPage에는 20이 들어갔는데
maxPage가 13이라면?
=> endPage값을 maxPage값으로 바꿔줘야 함
if(endPage>maxPage) {
endPage = maxPage;
}
2. VO에 담기 (미리 VO만들어 놓기)
PageInfo pi = new PageInfo(listCount, currentPage, pageLimit, boardLimit, maxPage, startPage, endPage);
4) Service로 값 보내기
ArrayList<Board> list = new BoardService().selectList(pi);
5) 응답화면 지정
request.setAttribute("list", list); //우리가 실제로 조회한 페이지에 보여질 10개의 게시글
request.setAttribute("pi", pi); //페이징 바를 만들 때 필요한 변수
request.getRequestDispatcher("views/board/boardListView.jsp").forward(request, response);
< com.kh.board.model.service>BoardService
1) listCount(게시글 수)
public int selectListCount() {
Connection conn = getConnection();
int listCount = new BoardDao().selectListCount(conn);
//SELECT문의 결과는 ResultSet
//게시글의 총 개수는 정수형
close(conn);
return listCount;
}
2) selectList(리스트 조회)
public ArrayList<Board> selectList(PageInfo pi) {
Connection conn = getConnection();
ArrayList<Board> list = new BoardDao().selectList(conn, pi);
close(conn);
return list;
}
< com.kh.board.model.dao>BoardDao
- DB와 연결
private Properties prop = new Properties();
public BoardDao() {
String fileName = BoardDao.class.getResource("/sql/board/board-mapper.xml").getPath();
try {
prop.loadFromXML(new FileInputStream(fileName));
} catch (IOException e) {
e.printStackTrace();
}
}
- selectListCount
public int selectListCount(Connection conn) {
//SELECT => ResultSet 하지만 반환은 int
int listCount = 0;
ResultSet rset = null;
PreparedStatement pstmt = 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;
}
- selectList
public ArrayList<Board> selectList(Connection conn, PageInfo pi) {
//SELECT문 => RESULTSET => 여러 행이므로 ArrayList<Borad>
ArrayList<Board> list = new ArrayList();
PreparedStatement pstmt = null;
ResultSet rset = null;
String sql = prop.getProperty("selectList");
try {
pstmt = conn.prepareStatement(sql);
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()) {
//하기전 String category필드 생성 후 생성자, getter,setter생성하기
list.add(new Board( rset.getInt("BOARD_NO"),
rset.getString("CATEGORY_NAME"),
rset.getString("BOARD_TITLE"),
rset.getString("USER_ID"),
rset.getInt("COUNT"),
rset.getDate("CREATE_DATE")
));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(rset);
close(pstmt);
}
return list;
}
* 인라인 뷰 활용
1) ORDER BY 순서가 가장 마지막인데 가장 처음에 실행이 필요함
일단 정렬해주는 SELECT문을 만듬 => 서브쿼리
2) 서브쿼리를 FROM절에 넣음 + ROWNUM
currentPage == 1 => 시작값1, 끝값10
currentPage == 2 => 시작값11, 끝값20
currentPage == 3 => 시작값21, 끝값30
시작값 = (currentPage - 1) * boardLimit + 1
끝값 = 시작값 + boardLimit-1
<쿼리문>
- selectListCount
<entry key="selectListCount">
SELECT
COUNT(*)
FROM
BOARD
WHERE
STATUS = 'Y'
AND
BOARD_TYPE = 1
</entry>
- selectList
<entry key="selectList">
SELECT *
FROM(SELECT
ROWNUM RNUM,
A.*
FROM
(SELECT
BOARD_NO,
CATEGORY_NAME,
BOARD_TITLE,
USER_ID,
COUNT,
CREATE_DATE
FROM
CATEGORY
JOIN
BOARD B USING(CATEGORY_NO)
JOIN
MEMBER ON(BOARD_WRITER = USER_NO)
WHERE
BOARD_TYPE = 1
AND
B.STATUS = 'Y'
ORDER
BY
CREATE_DATE DESC) A )
WHERE
RNUM BETWEEN ? AND ?
</entry>
<boardListView>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="java.util.ArrayList, com.kh.board.model.vo.Board, com.kh.common.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();
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>일반게시판</title>
<style>
.outer{
width: 1000px;
margin: auto;
background-color: #88A201;
margin-top: 5px;
color: white;
}
.list-area{
text-align : center;
border : 1px solid white
}
.list-area>tbody>tr:hover{
cursor : pointer;
background :rgb(200, 225, 131);
color : black;
}
</style>
</head>
<body>
<%@ include file="../common/menubar.jsp" %>
<div class="outer">
<br>
<h2 align="center">일반게시판</h2>
<br>
<table class="list-area" align="center">
<thead>
<th width="70">글번호</th>
<th width="80">카테고리</th>
<th width="300">제목</th>
<th width="100">작성자</th>
<th width="50">조회수</th>
<th width="100">작성일</th>
</thead>
<tbody>
<!-- 게시글 출력 : 게시글이 있는지 없는지 => isEmpty()이용해서 조건 부여 -->
<%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>
<br><br><br>
<div align="center" class="pasing-area">
<%if(startPage != currentPage){ %>
<button class="btn btn-sm btn-info" onclick="location.href='<%= contextPath%>/list.bo?cpage=<%=currentPage-1%>'"><</button>
<%} %>
<%for(int i = startPage; i<=endPage; i++){ %>
<%if(currentPage != i){ %>
<button class="btn btn-sm btn-info" onclick="location.href='<%=request.getContextPath() %>/list.bo?cpage=<%= i %>'"><%= i %></button>
<%}else{ %>
<button class="btn btn-sm btn-info" disabled><%= i %></button>
<%} %>
<%} %>
<%if(maxPage != currentPage){ %>
<button class="btn btn-sm btn-info" onclick="location.href='<%= contextPath%>/list.bo?cpage=<%=currentPage+1%>'">></button>
<%} %>
</div>
<br><br><br>
</div>
</body>
</html>
* 페이징 바 만들기 => 미리 PageInfo 객체에 담아온 변수를 이용해서 지정하기
3. 일반 게시글 작성 1
+ 더미데이터 넣을 때 엑셀 파일 넣기
우클릭 > 데이터 임포트 > 찾아보기로 파일 선택 > 다음 > *3 > 열정의에서 no를 nextval로 인식 못하고 문자로 인식하기 때문에 오류 - 그냥 무시 > 완료 > 행에 대한 삽입 실패 > 예 > 맨 마지막에 COMMIT 입력하고 스크립트 실행
<div align="right" style="width:840px">
<% if(loginUser != null){ %>
<a href="<%=contextPath %>/enrollForm.bo" class="btn btn-info btn-sm">글 작성</a>
<br><br>
<%} %>
</div>
- 로그인한 회원만 보여지는 버튼 : loginUser가 null이 아니면 보이도록
- 작성폼 띄우기 : 작성 폼 안에 DB에서 조회한 CATEGORY목록을 SELETE태그의 OPTION으로 띄워줄 것
ArrayList<Category> list = new BoardService().selectCategory();
request.setAttribute("list", list);
request.getRequestDispatcher("views/board/boardEnrollForm.jsp").forward(request, response);
=> 조회한 카테고리 list를 작성폼으로 넘겨줘서 카테고리 select태그 안에 넣어줄 예정
< com.kh.board.model.service>BoardService
public ArrayList<Category> selectCategory() {
Connection conn = getConnection();
ArrayList<Category> list = new BoardDao().selectCategoryList(conn);
close(conn);
return list;
}
< com.kh.board.model.dao>BoardDao
public ArrayList<Category> selectCategoryList(Connection conn) {
//SELECT문 => ResultSet => 7행 => List
ArrayList<Category> list = new ArrayList();
PreparedStatement pstmt = null;
ResultSet rset = null;
String sql = prop.getProperty("selectCategoryList");
try {
pstmt = conn.prepareStatement(sql);
rset = pstmt.executeQuery();
while(rset.next()) {
list.add(new Category(
rset.getInt("CATEGORY_NO"),
rset.getString("CATEGORY_NAME")
));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(rset);
close(pstmt);
}
return list;
}
- 가져온 list로 boardEnrollForm 카테고리만들기
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="java.util.ArrayList, com.kh.board.model.vo.Category"%>
<%
ArrayList<Category> list = (ArrayList<Category>)request.getAttribute("list");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>일반게시글 작성</title>
<style>
body{
box-sizing: border-box;
}
.outer{
width: 1000px;
margin: auto;
background-color: #88A201;
margin-top: 5px;
color: white;
}
#enroll-form input, #enroll-form textarea{
width: 100%;
box-sizing: border-box;
}
#enroll-form>table{
border: 1px solid wheat;
}
</style>
</head>
<body>
<%@ include file="../common/menubar.jsp" %>
<div class="outer">
<br>
<h2 align="center">일반게시글 작성하기</h2>
<br>
<form action="<%=contextPath%>/insert.bo" method="post" id="enroll-form" enctype="multipart/form-data">
<!-- 제목, 내용, 카테고리, 제출버튼, 글쓴이, 첨부파일 -->
<!-- 작성자의 회원번호를 hidden으로 같이 넘겨서 board테이블에 INSERT하게 만들 것 -->
<input type="hidden" name="userNo" value="<%=loginUser.getUserNo()%>">
<table align="center">
<tr>
<th width="150">카테고리</th>
<td width="600">
<select name="category">
<% for(Category c : list){ %>
<option value="<%= c.getCategoryNo()%>"><%= c.getCategoryName()%></option>
<%}%>
</select>
</td>
</tr>
<tr>
<th>제목</th>
<td><input type="text" name="title" required></td>
</tr>
<tr>
<th>내용</th>
<td>
<textarea name="content" style="resize: none;" rows="10" required></textarea>
</td>
</tr>
<tr>
<th>첨부파일</th>
<td><input type="file" name="upfile"></td>
</tr>
</table>
<br>
<div align="center">
<button type="submit">작성</button>
<button type="reset">취소</button>
</div>
</form>
</div>
</body>
</html>
* 파일을 첨부하는 요청을 할 때는 반드시 form태그에 enctype="multipart/form-data" ****오타주의