더보기
Servlet/JSP를 이용해 웹페이지 공지사항 기능을 제작해 보자
공지사항
✔️ 뼈대만 있는 게시판 ver
✔️ 공지사항 CRUD - 생성, 조회, 수정, 삭제
*공지사항 조회
menubar.jsp
<div class="nav-area" align="center">
<!-- (div.menu>a)*4 + Enter -->
<div class="menu"><a href="<%= contextPath %>">HOME</a></div>
<div class="menu"><a href="<%= contextPath %>/list.no">공지사항</a></div>
<div class="menu"><a href="">일반게시판</a></div>
<div class="menu"><a href="">사진게시판</a></div>
</div>
noticeListView.jsp 서블릿 생성
👉🏻 경로: \WebContent\views\notice\noticeListView.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="java.util.ArrayList, com.kh.notice.model.vo.Notice" %>
<%
// request에 담았던 list 키값을 뽑아오기 (== 공지사항 전체 리스트 조회 결과물)
ArrayList<Notice> list = (ArrayList<Notice>)request.getAttribute("list");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style>
.outer {
background-color: black;
color : white;
width : 1000px;
height : 500px;
margin : auto;
margin-top : 50px;
}
.list-area {
border : 1px solid white;
text-align : center;
}
.list-area>tbody>tr:hover {
background-color: grey;
cursor : pointer;
}
</style>
</head>
<body>
<%@ include file="../common/menubar.jsp" %>
<div class="outer">
<br>
<h2 align="center">공지사항</h2>
<br>
<!-- 공지사항은 관리자만 작성 가능하므로 조건 걸어 줘야 함 -->
<% if(loginUser != null && loginUser.getUserId().equals("admin")) { %>
<div style="width:850px;" align="right">
<a class="btn btn-secondary" href="<%= contextPath %>/enrollForm.no">글작성</a>
</div>
<% } %>
<table class="list-area" align="center">
<thead>
<tr>
<th>글번호</th>
<th width="400">글제목</th>
<th width="100">작성자</th>
<th>조회수</th>
<th width="100">작성일</th>
</tr>
</thead>
<tbody>
<!-- 보통 작성일 기준 내림차순, 즉 최신 글이 가장 위에 오게끔 구현함 -->
<!--
<tr>
<td>3</td>
<td>우리 서버는 안 터짐 ㅋㅋ</td>
<td>admin</td>
<td>120</td>
<td>2022-10-16</td>
</tr>
<tr>
<td>2</td>
<td>공지사항일까요</td>
<td>admin</td>
<td>70</td>
<td>2021-04-28</td>
</tr>
<tr>
<td>1</td>
<td>안녕하세요 공지사항 서비스 start</td>
<td>admin</td>
<td>256</td>
<td>2021-03-01</td>
</tr>
-->
<% if(list.isEmpty()) { %>
<!-- 리스트가 비어 있을 경우: 조회된 공지사항이 없을 경우 -->
<tr>
<td colspan="5">존재하는 공지사항이 없습니다.</td>
</tr>
<% } else { %>
<!-- 리스트가 비어 있지 않을 경우: 조회된 공지사항이 적어도 한 건이라도 존재할 경우 -->
<% for(Notice n : list) { %> <!-- 향상된 for문 -->
<tr>
<td><%= n.getNoticeNo() %></td>
<td><%= n.getNoticeTitle() %></td>
<td><%= n.getNoticeWriter() %></td>
<td><%= n.getCount() %></td>
<td><%= n.getCreateDate() %></td>
</tr>
<% } %>
<% } %>
</tbody>
</table>
</div>
</body>
</html>
NoticeListController.jsp 생성
👉🏻 경로: src\com\kh\notice\conroller\NoticeListController.java
👉🏻 url mapping: /list.no
package com.kh.notice.controller;
import java.io.IOException;
import java.util.ArrayList;
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.notice.model.service.NoticeService;
import com.kh.notice.model.vo.Notice;
/**
* Servlet implementation class NoticeListController
*/
@WebServlet("/list.no")
public class NoticeListController extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public NoticeListController() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 공지사항 전체 리스트 조회 후 조회 결과를 담아서 응답 페이지로 포워딩
// Service 단으로 요청 보낸 후 결과 받기
ArrayList<Notice> list = new NoticeService().selectNoticeList();
// 응답 페이지에 데이터를 보내기 전에 한번 출력
// System.out.println(list);
// 응답 페이지에서 필요로 하는 데이터를
request.setAttribute("list", list);
// 공지사항 리스트 페이지를 포워딩
request.getRequestDispatcher("views/notice/noticeListView.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);
}
}
Notice 클래스 생성
package com.kh.notice.model.vo;
import java.sql.Date;
public class Notice {
// 필드부
private int noticeNo; // NOTICE_NO NUMBER PRIMARY KEY,
private String noticeTitle; // NOTICE_TITLE VARCHAT2(100) NOT NULL,
private String noticeContent; // NOTICE_CONTENT VARCHAT2(4000) NOT NULL,
private String noticeWriter; // NOTICE_WRITER NUMBER NOT NULL,
// 조회 시 작성자 아이디값 "admin" / 작성하기 시 로그인한 회원번호 "1"
private int count; // COUNT NUMBER DEFAULT 0,
private Date createDate; // CREATE_DATE DATE DEFAULT SYSDATE NOT NULL,
private String status; // STATUS VARCHAR2(1) DEFAULT 'Y' CHECK (STATUS IN('Y', 'N')),
// 생성자부
// 기본 생성자
public Notice() {}
// 모든 필드에 대한 생성자
public Notice(int noticeNo, String noticeTitle, String noticeContent, String noticeWriter, int count,
Date createDate, String status) {
super();
this.noticeNo = noticeNo;
this.noticeTitle = noticeTitle;
this.noticeContent = noticeContent;
this.noticeWriter = noticeWriter;
this.count = count;
this.createDate = createDate;
this.status = status;
}
// 공지사항 전체 조회용 생성자
public Notice(int noticeNo, String noticeTitle, String noticeWriter, int count, Date createDate) {
super();
this.noticeNo = noticeNo;
this.noticeTitle = noticeTitle;
this.noticeWriter = noticeWriter;
this.count = count;
this.createDate = createDate;
}
// 메소드부
public int getNoticeNo() {
return noticeNo;
}
public void setNoticeNo(int noticeNo) {
this.noticeNo = noticeNo;
}
public String getNoticeTitle() {
return noticeTitle;
}
public void setNoticeTitle(String noticeTitle) {
this.noticeTitle = noticeTitle;
}
public String getNoticeContent() {
return noticeContent;
}
public void setNoticeContent(String noticeContent) {
this.noticeContent = noticeContent;
}
public String getNoticeWriter() {
return noticeWriter;
}
public void setNoticeWriter(String noticeWriter) {
this.noticeWriter = noticeWriter;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
@Override
public String toString() {
return "Notice [noticeNo=" + noticeNo + ", noticeTitle=" + noticeTitle + ", noticeContent=" + noticeContent
+ ", noticeWriter=" + noticeWriter + ", count=" + count + ", createDate=" + createDate + ", status="
+ status + "]";
}
}
NoticeService 클래스 생성
package com.kh.notice.model.service;
import java.sql.Connection;
import java.util.ArrayList;
// import com.kh.common.JDBCTemplate;
import static com.kh.common.JDBCTemplate.*; // JDBCTemplate 클래스의 모든 메소드들을 그냥 가져다 쓰겠음!
import com.kh.notice.model.dao.NoticeDao;
import com.kh.notice.model.vo.Notice;
public class NoticeService {
// 공지사항 전체 조회용 서비스
public ArrayList<Notice> selectNoticeList() {
// 1) Connection 객체 생성
Connection conn = /* JDBCTemplate.*/ getConnection();
// 2) 만들어진 Connection 객체와 전달값을 DAO로 넘기면서 요청 처리 후 결과받기
ArrayList<Notice> list = new NoticeDao().selectNoticeList(conn);
// 3) 트랜잭션 처리 => SELECT문을 실행할 것이기 때문에 패스
// 4) Connection 객체 반납
/* JDBCTemplate.*/ close(conn);
// 5) 결과 반환
return list;
}
}
NoticeDao 클래스 생성
package com.kh.notice.model.dao;
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 static com.kh.common.JDBCTemplate.*;
import com.kh.notice.model.vo.Notice;
public class NoticeDao {
private Properties prop = new Properties();
public NoticeDao() {
// 다른 클래스에서 NoticeDao를 호출하면 해당 기본 메소드가 먼저 실행되므로
// notice-mapper 파일의 sql문을 읽어들일 준비를 여기서 해 줌
// 어느 경로의 파일을 읽어올 것인지 제시
String fileName = NoticeDao.class.getResource("/sql/notice/notice-mapper.xml").getPath();
// 파일 읽어들이기
try {
prop.loadFromXML(new FileInputStream(fileName));
} catch (IOException e) { // multi catch로 부모 타입인 IOException를 예외처리 할 수 있도록 함
e.printStackTrace();
}
}
public ArrayList<Notice> selectNoticeList(Connection conn) {
// SELECT문 => ResultSet 객체 (공지사항 전체 조회이기 때문에 여러 건이 조회될 가능성이 훨씬 높음)
// 즉, 여러 행 조회이므로 ArrayList<Notice>로 받아 줘야 함
// 1) 필요한 변수 먼저 세팅
ArrayList<Notice> list = new ArrayList<>();
PreparedStatement pstmt = null;
ResultSet rset = null;
String sql = prop.getProperty("selctNoticeList");
try {
// 2) PreparedStatement 객체 생성
pstmt = conn.prepareStatement(sql);
// 3_1) 미완성된 쿼리문 완성시키기
// => 쿼리문 자체가 완성되어 있고, 채울 값 없으므로 패스
// 3_2) 쿼리문 실행 후 결과 받기
rset = pstmt.executeQuery();
// 4) rset으로부터 더 이상 뽑을 값이 없을 때까지 반복 돌려가며 조회된 내용물을 가공하기
while(rset.next()) {
list.add(new Notice(rset.getInt("NOTICE_NO"),
rset.getString("NOTICE_TITLE"),
rset.getString("USER_ID"),
rset.getInt("COUNT"),
rset.getDate("CREATE_DATE")));
// 위와 같은 매개변수 3가지의 생성자가 없기 때문에 빨간줄! 생성자 추가해 주면 없어짐
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 5) 자원 반납
// JDBCTemplate import를 static으로 바꾸고 별 찍어 주면 클래스명 제시하지 않아도 쓸 수 있음
/* JDBCTemplate.*/ close(rset);
/* JDBCTemplate.*/ close(pstmt);
}
// 6) 결과 반환
return list; // 성공: list의 내용물이 있음, 실패: list.isEmpty() == true
}
}
notice-mapper.xml
<!--
SELECT * 찍으면 나중에 성능 문제가 생길 수 있으므로
정말 모두 필요한 게 아니라면 필요한 컬럼명을 직접 제시하는 것을 권장함!
-->
<entry key="selctNoticeList">
SELECT NOTICE_NO, NOTICE_TITLE, USER_ID, COUNT, CREATE_DATE
FROM NOTICE N
JOIN MEMBER ON (NOTICE_WRITER = USER_NO)
WHERE N.STATUS = 'Y'
ORDER BY NOTICE_NO DESC
</entry>
*공지사항 작성
관리자 계정으로 접속해서 공지사항을 작성
noticeEnrollForm.jsp 생성
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style>
.outer {
background-color: black;
color : white;
width : 1000px;
height : 500px;
margin : auto;
margin-top : 50px;
}
#enroll-form>table { border : 1px solid white; }
#enroll-form input, #enroll-form textarea {
width : 100%;
box-sizing : border-box;
}
</style>
</head>
<body>
<%@ include file="../common/menubar.jsp" %>
<div class="outer">
<br>
<h2 align="center">공지사항 작성하기</h2>
<br>
<!--
현재 나의 위치: http:// localhost:8888/jsp/enrollForm.no
공지사항 작성 요청을 보낼 url: http://localhost:8888/jsp/insert.no
절대 경로: /jsp/insert.no
상대 경로: insert.no
-->
<form id="enroll-form" action="<%= contextPath %>/insert.no" method="post">
<!--
현재 로그인한 사용자의 정보를 알아내는 방법
1. input type="hidden"으로 애초에 넘기기
2. session으로부터 얻어내기
-->
<input type="hidden" name="userNo" value="<%= loginUser.getUserNo() %>">
<table align="center">
<tr>
<th width="50">제목</th>
<td width="350"><input type="text" name="title" required></td>
</tr>
<tr>
<th>내용</th>
<td></td>
</tr>
<tr>
<td colspan="2">
<textarea name="content" rows="10" name="content" style="resize:none;" required></textarea>
</td>
</tr>
</table>
<br><br>
<div align="center">
<button type="submit">등록하기</button>
<button type="button" onclick="history.back();">뒤로가기</button>
<!-- history.back(): 이전 페이지로 돌아가기-->
</div>
</form>
</div>
</body>
</html>
NoticeEnrollFormController 생성
👉🏻 작성 폼 화면을 띄워 주는 역할
package com.kh.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;
/**
* Servlet implementation class NoticeEnrollFormController
*/
@WebServlet("/enrollForm.no")
public class NoticeEnrollFormController extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public NoticeEnrollFormController() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.getRequestDispatcher("views/notice/noticeEnrollForm.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);
}
}
NoticeInsertController 생성
👉🏻 새롭게 게시글을 insert 할 수 있는 컨트롤러
package com.kh.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.notice.model.service.NoticeService;
import com.kh.notice.model.vo.Notice;
/**
* Servlet implementation class NoticeInsertController
*/
@WebServlet("/insert.no")
public class NoticeInsertController extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public NoticeInsertController() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1) 인코딩 설정
request.setCharacterEncoding("UTF-8");
// 2) 요청 시 전달값을 뽑아서 변수 및 객체에 담기
// 뽑아야 할 값
// title: 글 제목
String noticeTitle = request.getParameter("title");
// content : 글 내용
String noticeContent = request.getParameter("content");
// 추가적으로 필요한 데이터: 작성자의 회원번호 (userNo)
String userNo = request.getParameter("userNo"); // "1" => 문자열로써의 회원번호 받아냄
// noticeWriter 필드는 애초에 String 타입으로 정의해 뒀기 때문에 굳이 파싱을 하지 않음!
Notice n = new Notice();
n.setNoticeTitle(noticeTitle);
n.setNoticeContent(noticeContent);
n.setNoticeWriter(userNo);
// 3) Service 단으로 넘기면서 요청 후 결과 받기
int result = new NoticeService().insertNotice(n);
// 4) 결과에 따른 응답 페이지 지정
if(result > 0) { // 성공 => 성공 문구 alert, 공지사항 리스트 조회 페이지로 url 요청
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);
}
}
NoticeService
// 공지사항 작성용 서비스
public int insertNotice(Notice n) {
// 1) Connection 객체 생성
Connection conn = getConnection();
// 2) conn, n을 넘기면서 DAO에 요청 후 결과받기
int result = new NoticeDao().insertNotice(conn, n);
// 3) 트랜잭션 처리
if(result > 0) {
commit(conn);
} else {
rollback(conn);
}
// 4) 반납
close(conn);
// 5) 반환
return result;
}
NoticeDao
public int insertNotice(Connection conn, Notice n) {
// INSERT문 => int (처리된 행의 개수)
// 1) 필요한 변수 세팅
int result = 0;
PreparedStatement pstmt = null;
String sql = prop.getProperty("insertNotice");
try {
// 2) pstmt 객체 생성
pstmt = conn.prepareStatement(sql);
// 3_1) 쿼리문 완성
pstmt.setString(1, n.getNoticeTitle());
pstmt.setString(2, n.getNoticeContent());
pstmt.setInt(3, Integer.parseInt(n.getNoticeWriter())); // "1" -> 1
// 3_2) 실행 후 결과 받기
result = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 4) 자원 반납
close(pstmt);
}
// 5) 반환
return result;
}
notice-mapper.xml
<entry key="insertNotice">
INSERT INTO NOTICE (NOTICE_NO
, NOTICE_TITLE
, NOTICE_CONTENT
, NOTICE_WRITER)
VALUES (SEQ_NNO.NEXTVAL
,?
,?
,?)
</entry>
*공지사항 상세 조회 (정적 코딩 ver.)
조회 수 up, 동적 코딩 ver. 은 따로 진행
noticeListView
<script>
$(function() {
$(".list-area>tbody>tr").click(function() {
// console.log("클릭됨");
// 클릭했을 때 공지사항 상세보기 페이지를 요청
// 단, 해당 공지사항의 게시글 번호를 넘겨야지만 그 게시글만 조회 가능(primary key)
// => 해당 클릭된 tr 요소의 자손들 중에서 첫 번째 td 영역의 내용
var nno = $(this).children().eq(0).text();
// console.log(nno);
// 게시글 번호를 넘기면서 url 요청
// 요청할 url주소?키=밸류&키=밸류&...
// ? 뒤의 내용물을 쿼리스트링이라고 부름, 그 쿼리스트링을 직접 만들어서 넘길 예정
// => url 주소상에 쿼리스트링이 노출 == GET 방식
// /jsp/detail.no?nno=클릭했을때의글번호
location.href = "<%= contextPath %>/detail.no?nno=" + nno;
})
})
</script>
noticeDetailView
👉🏻 경로: WebContent\views\notice\noticeDetailView.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style>
.outer {
background-color: black;
color : white;
width : 1000px;
height : 500px;
margin : auto;
margin-top : 50px;
}
#detail-area { border : 1px solid white;}
</style>
</head>
<body>
<%@ include file="../common/menubar.jsp" %>
<div class="outer">
<br>
<h2 align="center">공지사항 상세보기</h2>
<br>
<table id="detail-area" align="center" border="1">
<tr>
<th width="70">제목</th>
<td width="350" colspan="3">공지사항 제목입니다.</td>
</tr>
<tr>
<th>작성자</th>
<td>admin</td>
<th>작성일</th>
<td>2022-08-10</td>
</tr>
<tr>
<th>내용</th>
<td colspan="3">
<p style="height:150px;">
xxxxxxxxxxx
</p>
</td>
</tr>
</table>
<br><br>
<div align="center">
<a href="<%= contextPath %>/list.no" class="btn btn-secondary btn-sm">목록가기</a>
<!-- 현재 로그인한 사용자가 해당 글을 작성한 경우에만 보여지게끔 -->
<a href="" class="btn btn-warning btn-sm">수정하기</a>
<a href="" class="btn btn-danger btn-sm">삭제하기</a>
</div>
</div>
</body>
</html>
NoticeDetailController
👉🏻 url mapping: /detail.no
package com.kh.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;
/**
* Servlet implementation class NoticeDetailController
*/
@WebServlet("/detail.no")
public class NoticeDetailController extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public NoticeDetailController() {
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"));
// System.out.println(noticeNo);
// 공지사항 상세보기 페이지 포워딩
request.getRequestDispatcher("views/notice/noticeDetailView.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);
}
}
*공지사항 상세 조회(동적 코딩 ver.)
조회 수 up, 동적 코딩
NoticeService
// 공지사항 조회수 증가용 서비스
public int increaseCount(int noticeNo) {
// 1) Connection 생성
Connection conn = getConnection();
// 2) conn, noticeNo을 DAO로 넘기면서 요청 후 결과 받기
int result = new NoticeDao().increaseCount(conn, noticeNo);
// 3) 트랜잭션 처리
if(result > 0) {
commit(conn);
} else {
rollback(conn);
}
// 4) conn 반납
close(conn);
// 5) 결과 리턴
return result;
}
// 게시글 상세조회용 서비스
public Notice selectNotice(int noticeNo) {
// 1) Connection 생성
Connection conn = getConnection();
// 2) conn, noticeNo을 넘기면서 DAO에 요청 처리 후 결과 받기
Notice n = new NoticeDao().selectNotice(conn, noticeNo);
// 3) 트랜잭션 처리 => 패스
// 4) conn 반납
close(conn);
// 5) 결과 리턴
return n;
}
NoticeDao
public int increaseCount(Connection conn, int noticeNo) {
// UPDATE문 => int (처리된 행의 개수)
// 1) 필요한 변수 먼저 세팅
int result = 0;
PreparedStatement pstmt = null;
String sql = prop.getProperty("increaseCount");
try {
// 2) pstmt 객체 생성
pstmt = conn.prepareStatement(sql);
// 3_1) 쿼리문 완성
pstmt.setInt(1, noticeNo);
// 3_2) 실행 후 결과받기
result = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 4) 자원 반납
close(pstmt);
}
// 5) 결과 반환
return result;
}
public Notice selectNotice(Connection conn, int noticeNo) {
// SELECT문 => ResultSet 객체(Primary key 기준으로 조회하기 때문에 많아 봤자 1건) => Notice 객체
// 1) 필요한 변수 먼저 세팅
Notice n = null;
PreparedStatement pstmt = null;
ResultSet rset = null;
String sql = prop.getProperty("selectNotice");
try {
// 2) pstmt 객체 생성
pstmt = conn.prepareStatement(sql);
// 3_1) 쿼리문 완성시키기
pstmt.setInt(1, noticeNo);
// 3_2) 실행 후 결과 받기
rset = pstmt.executeQuery();
// 4) rset으로부터 조회된 데이터 뽑기
if(rset.next()) {
n = new Notice(rset.getInt("NOTICE_NO")
, rset.getString("NOTICE_TITLE")
, rset.getString("NOTICE_CONTENT")
, rset.getString("USER_ID")
, rset.getDate("CREATE_DATE"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 5) 자원 반납
close(rset);
close(pstmt);
}
// 6) 결과 반환
return n;
}
notice-mapper.xml
<!-- 오라클에서는 복합대입연산자(+=) 사용 불가함! -->
<entry key="increaseCount">
UPDATE NOTICE
SET COUNT = COUNT + 1
WHERE NOTICE_NO = ?
AND STATUS = 'Y'
</entry>
<entry key="selectNotice">
SELECT NOTICE_NO
, NOTICE_TITLE
, NOTICE_CONTENT
, USER_ID
, CREATE_DATE
FROM NOTICE N
JOIN MEMBER ON (NOTICE_WRITER = USER_NO)
WHERE NOTICE_NO = ?
AND N.STATUS = 'Y'
</entry>
Notice 생성자 추가
👉🏻 공지사항 삭제 조회용 생성자
// 공지사항 상세 조회용 생성자
public Notice(int noticeNo, String noticeTitle, String noticeContent, String noticeWriter, Date createDate) {
super();
this.noticeNo = noticeNo;
this.noticeTitle = noticeTitle;
this.noticeContent = noticeContent;
this.noticeWriter = noticeWriter;
this.createDate = createDate;
}
NoticeDetailController
package com.kh.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.notice.model.service.NoticeService;
import com.kh.notice.model.vo.Notice;
/**
* Servlet implementation class NoticeDetailController
*/
@WebServlet("/detail.no")
public class NoticeDetailController extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public NoticeDetailController() {
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"));
// System.out.println(noticeNo);
// 조회수 증가용 서비스 먼저 호출
int result = new NoticeService().increaseCount(noticeNo);
// 조회 수가 잘 증가되었다면 1 => 게시글 상세 조회 요청
// 아니라면 0 => 실패 처리
if(result > 0) { // 성공
// 게시글 상세조회 요청 후 noticeDetailView.jsp가 보여지도록 포워딩
Notice n = new NoticeService().selectNotice(noticeNo);
// selectNoticeList 아님!
// System.out.println(n);
request.setAttribute("n", n);
// 공지사항 상세보기 페이지 포워딩
request.getRequestDispatcher("views/notice/noticeDetailView.jsp").forward(request, response);
} 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);
}
}
noticeDetailView
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" import="com.kh.notice.model.vo.Notice" %>
<%
// 조회한 공지사항 게시글
Notice n = (Notice)request.getAttribute("n");
// 글번호, 글제목, 글내용, 작성자 아이디, 작성일
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style>
.outer {
background-color: black;
color : white;
width : 1000px;
height : 500px;
margin : auto;
margin-top : 50px;
}
#detail-area { border : 1px solid white;}
</style>
</head>
<body>
<%@ include file="../common/menubar.jsp" %>
<div class="outer">
<br>
<h2 align="center">공지사항 상세보기</h2>
<br>
<table id="detail-area" align="center" border="1">
<tr>
<th width="70">제목</th>
<!-- <td width="350" colspan="3">공지사항 제목입니다.</td> -->
<td width="350" colspan="3"><%= n.getNoticeTitle() %></td>
</tr>
<tr>
<th>작성자</th>
<!-- <td>admin</td> -->
<td><%= n.getNoticeWriter() %></td>
<th>작성일</th>
<!-- <td>2022-08-10</td> -->
<td><%= n.getCreateDate() %></td>
</tr>
<tr>
<th>내용</th>
<td colspan="3">
<!-- <p style="height:150px;">
xxxxxxxxxxx
</p> -->
<p style="height:150px;">
<%= n.getNoticeContent() %>
</p>
</td>
</tr>
</table>
<br><br>
<div align="center">
<a href="<%= contextPath %>/list.no" class="btn btn-secondary btn-sm">목록가기</a>
<!-- 현재 로그인한 사용자가 해당 글을 작성한 경우에만 보여지게끔 -->
<a href="" class="btn btn-warning btn-sm">수정하기</a>
<a href="" class="btn btn-danger btn-sm">삭제하기</a>
</div>
</div>
</body>
</html>
noticeListView.jsp
👉🏻 기존 table이 끝나는 지점에 클릭 이벤트 추가
👉🏻 정적 코딩 때와 코드 똑같으나 주석 없는 버전으로 한 번 더 적었을 뿐임!
<script>
$(function() {
<!-- 게시글에 클릭 버튼 걸기 -->
$(".list-area>tbody>tr").click(function() {
<!-- 선택된 td 태그의 글 번호 내용물 추출 -->
location.href = "<%= contextPath %>/detail.no?nno=" + $(this).children().eq(0).text();
});
});
</script>
*공지사항 수정
noticeDetailView
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" import="com.kh.notice.model.vo.Notice" %>
<%
// 조회한 공지사항 게시글
Notice n = (Notice)request.getAttribute("n");
// 글번호, 글제목, 글내용, 작성자 아이디, 작성일
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style>
.outer {
background-color: black;
color : white;
width : 1000px;
height : 500px;
margin : auto;
margin-top : 50px;
}
#detail-area { border : 1px solid white;}
</style>
</head>
<body>
<%@ include file="../common/menubar.jsp" %>
<div class="outer">
<br>
<h2 align="center">공지사항 상세보기</h2>
<br>
<table id="detail-area" align="center" border="1">
<tr>
<th width="70">제목</th>
<!-- <td width="350" colspan="3">공지사항 제목입니다.</td> -->
<td width="350" colspan="3"><%= n.getNoticeTitle() %></td>
</tr>
<tr>
<th>작성자</th>
<!-- <td>admin</td> -->
<td><%= n.getNoticeWriter() %></td>
<th>작성일</th>
<!-- <td>2022-08-10</td> -->
<td><%= n.getCreateDate() %></td>
</tr>
<tr>
<th>내용</th>
<td colspan="3">
<!-- <p style="height:150px;">
xxxxxxxxxxx
</p> -->
<p style="height:150px;">
<%= n.getNoticeContent() %>
</p>
</td>
</tr>
</table>
<br><br>
<div align="center">
<a href="<%= contextPath %>/list.no" class="btn btn-secondary btn-sm">목록가기</a>
<% if(loginUser != null && loginUser.getUserId().equals(n.getNoticeWriter())) { %>
<!-- 현재 로그인한 사용자가 해당 글을 작성한 경우에만 보여지게끔 -->
<a href="<%= contextPath %>/updateForm.no?nno=<%= n.getNoticeNo() %>" class="btn btn-warning btn-sm">수정하기</a>
<a href="" class="btn btn-danger btn-sm">삭제하기</a>
<% } %>
</div>
</div>
</body>
</html>
noticeUpdateForm.jsp 생성
👉🏻 include 후 noticeEnrollForm.jsp 에서 div, style 복붙 후 수정
👉🏻 보통 페이지의 작성, 수정 페이지는 비슷하니까!
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" import="com.kh.notice.model.vo.Notice"%>
<%
//해당 공지사항 게시글 뽑기
Notice n = (Notice)request.getAttribute("n");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style>
.outer {
background-color: black;
color: white;
width: 1000px;
height: 500px;
margin:auto;
margin-top: 50px;
}
#update-form>table{ border: 1px solid white; }
#update-form input, #update-form textarea{
width: 100%;
box-sizing: border-box;
}
</style>
</head>
<body>
<%@ include file="../common/menubar.jsp" %>
<div class="outer">
<br>
<h2 align="center">공지사항 수정하기</h2>
<br>
<!--
현재 나의 위치 : http://localhost:8888/jsp/updateForm.no?nno=x
공지사항 수정 요청을 보낼 url : http://localhost:8888/jsp/update.no
(작성 크기 제한이 없는 post방식으로 넘기기 때문에 ?nno=x 와 같은 get방식은 이용하지 못함)
절대경로 : /jsp/update.no
상대경로 : update.no
-->
<form id="update-form" action="<%= contextPath %>/update.no" method="post">
<input type="hidden" name="nno" value="<%= n.getNoticeNo()%>">
<table align="center">
<tr>
<th width="50">제목</th>
<td width="350"><input type="text" name="title" required value="<%= n.getNoticeTitle()%>"></td>
</tr>
<tr>
<th>내용</th>
<td></td>
</tr>
<td colspan="2">
<textarea name="content" rows="10" style="resize:none;" required><%= n.getNoticeContent()%></textarea>
</td>
</table>
<br><br>
<div align="center">
<button type="submit">수정하기</button>
<button type="button" onclick="history.back();">뒤로가기</button>
<!-- history.back() : 이 전 페이지로 돌아가주는 history객체의 back메소드 -->
</div>
</form>
</div>
</body>
</html>
NoticeUpdateFormController 생성
👉🏻 url mapping: /updateForm.no
package com.kh.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.notice.model.service.NoticeService;
import com.kh.notice.model.vo.Notice;
/**
* Servlet implementation class NoticeUpdateFormController
*/
@WebServlet("/updateForm.no")
public class NoticeUpdateFormController extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public NoticeUpdateFormController() {
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")); // "1" -> 1
// 수정 페이지에서 기존의 제목과 기존의 내용이 보여지게끔 다시 조회
Notice n = new NoticeService().selectNotice(noticeNo);
// 글번호, 글제목, 글내용, 작성자 아이디, 작성일
request.setAttribute("n", n);
// 공지사항 수정 페이지 포워딩
request.getRequestDispatcher("views/notice/noticeUpdateForm.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);
}
}
NoticeUpdateController 생성
package com.kh.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.notice.model.service.NoticeService;
import com.kh.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 {
// 인코딩 설정
request.setCharacterEncoding("UTF-8");
// nno: 글번호 => 어느 글을 수정할 것인지
int noticeNo = Integer.parseInt(request.getParameter("nno"));
// title: 글 제목 => 바꿀 제목
String noticeTitle = request.getParameter("title");
// content: 글 내용 => 바꿀 내용
String noticeContent = request.getParameter("content");
Notice n = new Notice();
n.setNoticeNo(noticeNo);
n.setNoticeTitle(noticeTitle);
n.setNoticeContent(noticeContent);
int result = new NoticeService().updateNotice(n);
if(result > 0) { // 성공 => session에 alertMsg 담기, 해당 게시글의 상세조회 페이지로 url 요청
request.getSession().setAttribute("alertMsg", "성공적으로 공지사항이 수정되었습니다.");
// /jsp/detail.no?nno=해당글번호
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);
}
}
NoticeService
// 공지사항 수정용 서비스
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;
}
NoticeDao
public int updateNotice(Connection conn, Notice n) {
// UPDATE문 => int (처리된 행의 개수)
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;
}
notice-mapper.xml
<entry key="updateNotice">
UPDATE NOTICE
SET NOTICE_TITLE = ?
, NOTICE_CONTENT = ?
WHERE NOTICE_NO = ?
AND STATUS = 'Y'
</entry>
*공지사항 삭제
noticeDetailView
<a href="<%= contextPath %>/delete.no?nno=<%= n.getNoticeNo() %>" class="btn btn-danger btn-sm">삭제하기</a>
NoticeDeleteController 서블릿 생성
package com.kh.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.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) { // 성공 => alertMsg 담기, 공지사항 리스트 페이지로 url 요청
request.getSession().setAttribute("alertMsg", "성공적으로 공지사항이 삭제되었습니다.");
// /jsp/list.no
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);
}
}
noticeService
// 공지사항 삭제용 서비스
public int deleteNotice(int noticeNo) {
Connection conn = getConnection();
int result = new NoticeDao().deleteNotice(conn, noticeNo);
if(result > 0) {
commit(conn);
} else {
rollback(conn);
}
return result;
}
noticeDao
public int deleteNotice(Connection conn, int noticeNo) {
// UPDATE문 => int (처리된 행의 갯수)
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.xml
<entry key="deleteNotice">
UPDATE NOTICE
SET STATUS = 'N'
WHERE NOTICE_NO = ?
</entry>