2020년 01월 11일
업데이트:
게시글 삭제
- DB 상태 변경
boardView.jsp
<script>
// 삭제 버튼 이벤트
$("#deleteBtn").on("click", function(){
if(confirm("정말 삭제 하시겠습니까?")){
location.href = "delete.do?no=${board.boardNo}";
}
});
</script>
BoardController.java
// 게시글 삭제 Controller ************************************************************************
else if(command.equals("/delete.do")) {
// 게시글 번호 얻어오기
int boardNo = Integer.parseInt(request.getParameter("no"));
// 게시글 삭제(게시글 상태 -> 'N') 비즈니스 로직 수행 후 결과 반환
int result = service.deleteBoard(boardNo);
// 비즈니스 로직 결과에 따라
// "댓글 삭제 성공" / "댓글 삭제 실패" 메세지를 전달
if(result > 0) {
swalIcon = "success";
swalTitle = "게시글 삭제 성공";
path = "list.do?cp=1";
}else {
swalIcon = "error";
swalTitle = "게시글 삭제 실패";
path = "view.do?no=" + boardNo;
}
// 삭제 성공 시 : 게시글 목록
// 삭제 실패 시 : 삭제 시도한 게시글 상세 조회 페이지 redirect
request.getSession().setAttribute("swalIcon", swalIcon);
request.getSession().setAttribute("swalTitle", swalTitle);
response.sendRedirect(path);
}
BoardService.java
/** 게시글 삭제 Service
* @param boardNo
* @return result
* @throws Exception
*/
public int deleteBoard(int boardNo) throws Exception {
Connection conn = getConnection();
int result = dao.deleteBoard(conn, boardNo);
if(result > 0) commit(conn);
else rollback(conn);
return result;
}
board-query.xml
<!-- 게시글 삭제 -->
<entry key="deleteBoard">
UPDATE BOARD SET
BOARD_STATUS = 'N'
WHERE BOARD_NO = ?
</entry>
BoardDAO.java
/** 게시글 삭제 DAO
* @param conn
* @param boardNo
* @return result
* @throws Exception
*/
public int deleteBoard(Connection conn, int boardNo) throws Exception {
int result = 0;
String query = prop.getProperty("deleteBoard");
try {
pstmt = conn.prepareStatement(query);
pstmt.setInt(1, boardNo);
result = pstmt.executeUpdate();
}finally {
close(pstmt);
}
return result;
}
상세 조회 페이지에 댓글 추가하기
- 상세 조회 페이지 하단 부분에 댓글 추가
GSON 사용을 위해 라이브러리 추가하기
reply.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<style>
/*댓글*/
.replyWrite>table {
width: 90%;
margin-top: 100px;
}
#replyContentArea {
width: 90%;
}
#replyContentArea > textarea {
resize: none;
width: 100%;
}
#replyBtnArea {
width: 100px;
text-align: center;
}
.rWriter {
display: inline-block;
margin-right: 30px;
vertical-align: top;
}
.rDate {
display: inline-block;
font-size: 0.5em;
color: gray;
}
#replyListArea {
list-style-type: none;
}
.rContent, .replyBtnArea {
height: 100%;
width: 100%;
}
.replyBtnArea {
text-align: right;
}
.replyUpdateContent {
resize: none;
width: 100%;
}
.reply-row{
border-top : 1px solid #ccc;
padding : 15px 0;
}
</style>
<div id="reply-area ">
<!-- 댓글 작성 부분 -->
<div class="replyWrite">
<table align="center">
<tr>
<td id="replyContentArea">
<textArea rows="3" id="replyContent"></textArea>
</td>
<td id="replyBtnArea">
<button class="btn btn-primary" id="addReply">
댓글<br>등록
</button>
</td>
</tr>
</table>
</div>
<!-- 댓글 출력 부분 -->
<div class="replyList mt-5 pt-2">
<ul id="replyListArea">
<!-- 로그인 x 또는 댓글 작성자가 아닌 회원의 화면 -->
<li class="reply-row">
<div>
<p class="rWriter">작성자</p>
<p class="rDate">작성일 : 2021.01.11 10:30</p>
</div>
<p class="rContent">댓글 내용1</p>
</li>
<!-- 로그인한 회원이 댓글 작성자인 경우 -->
<li class="reply-row">
<div>
<p class="rWriter">작성자</p>
<p class="rDate">작성일 : 2021.01.11 10:30</p>
</div>
<p class="rContent">댓글 내용2</p>
<div class="replyBtnArea">
<button class="btn btn-primary btn-sm ml-1" id="updateReply" onclick="showUpdateReply(2, this)">수정</button>
<button class="btn btn-primary btn-sm ml-1" id="deleteReply" onclick="deleteReply(2)">삭제</button>
</div>
</li>
</ul>
</div>
</div>
boardView.jsp
상세 조회 페이지에서 댓글이 보이고 싶은 부분에 reply.jsp를 연결한다.
<jsp:include page="reply.jsp"></jsp:include>
SQL 테이블
- REPLY 테이블
- V_REPLY View
댓글 목록 조회
- 댓글 목록 조회
- DB 확인
- reply.jsp
<script>
var loginMemberId = "${loginMember.memberId}";
var parentBoardNo = ${board.boardNo};
// 페이지 로딩 완료 시 댓글 목록 호출
$(function(){
selectReplyList();
});
// 해당 게시글 댓글 목록 조회 함수(ajax)
function selectReplyList(){
$.ajax({
url : "${contextPath}/reply/selectList.do",
data : {"parentBoardNo" : parentBoardNo },
type : "post",
dataType : "JSON",
success : function(rList){
//console.log(rList);
$("#replyListArea").html("");
$.each(rList, function(index, item){
var li = $("<li>").addClass("reply-row");
var rWriter = $("<p>").addClass("rWriter").text(item.memberId);
var rDate = $("<p>").addClass("rDate").text("작성일 : " + item.replyCreateDate);
var div = $("<div>");
div.append(rWriter).append(rDate);
var rContent = $("<p>").addClass("rContent").html(item.replyContent);
li.append(div).append(rContent);
// 현재 댓글의 작성자와 로그인한 멤버의 아이디가 같을 때 버튼 추가
if(item.memberId == loginMemberId){
// 수정, 삭제 버튼 영역
var replyBtnArea = $("<div>").addClass("replyBtnArea");
// ** 추가되는 댓글에 onclick 이벤트를 부여하여 버튼 클릭 시 수정, 삭제를 수행할 수 있는 함수를 이벤트 핸들러로 추가함.
var showUpdate = $("<button>").addClass("btn btn-primary btn-sm ml-1").text("수정").attr("onclick", "showUpdateReply("+item.replyNo+", this)");
var deleteReply = $("<button>").addClass("btn btn-primary btn-sm ml-1").text("삭제").attr("onclick", "deleteReply("+item.replyNo+")");
replyBtnArea.append(showUpdate).append(deleteReply);
li.append(replyBtnArea);
}
$("#replyListArea").append(li);
});
},
error : function(){
console.log("댓글 목록 조회 실패");
}
});
}
</script>
- ReplyController.java
// 댓글 목록 조회 Controller *************************************
if(command.equals("/selectList.do")) {
int parentBoardNo = Integer.parseInt(request.getParameter("parentBoardNo"));
List<Reply> rList = service.selectList(parentBoardNo);
Gson gson = new GsonBuilder().setDateFormat("yyyy년 MM월 dd일 HH:mm").create();
gson.toJson(rList, response.getWriter()); // rList가 자동으로 gson 형태로 변환된다.
}
- ReplyService.java
/** 댓글 목록 조회 Service
* @param parentBoardNo
* @return rList
* @throws Exception
*/
public List<Reply> selectList(int parentBoardNo) throws Exception{
Connection conn = getConnection();
List<Reply> rList = dao.selectList(conn, parentBoardNo);
close(conn);
return rList;
}
- reply-query.xml
<!-- 댓글 목록 조회 -->
<entry key="selectList">
SELECT REPLY_NO, REPLY_CONTENT, REPLY_CREATE_DT, MEMBER_ID
FROM V_REPLY
WHERE REPLY_STATUS = 'Y'
AND PARENT_BOARD_NO = ?
</entry>
- ReplyDAO.java
/** 댓글 목록 조회 DAO
* @param conn
* @param parentBoardNo
* @return rList
* @throws Exception
*/
public List<Reply> selectList(Connection conn, int parentBoardNo) throws Exception{
List<Reply> rList = null;
String query = prop.getProperty("selectList");
try {
pstmt = conn.prepareStatement(query);
pstmt.setInt(1, parentBoardNo);
rset = pstmt.executeQuery();
rList = new ArrayList<Reply>();
while(rset.next()) {
Reply reply = new Reply();
reply.setReplyNo(rset.getInt("REPLY_NO"));
reply.setReplyContent(rset.getString("REPLY_CONTENT"));
reply.setReplyCreateDate(rset.getTimestamp("REPLY_CREATE_DT"));
reply.setMemberId(rset.getString("MEMBER_ID"));
rList.add(reply);
}
} finally {
close(rset);
close(pstmt);
}
return rList;
}
댓글 등록
- 미 로그인 시
- 로그인 O, 댓글 내용 미 작성 시
-
댓글 작성 성공 시
- reply.jsp
<script>
// 댓글 등록 (ajax)
$("#addReply").on("click", function(){
// 댓글 내용을 얻어와서 변수에 저장
var replyContent = $("#replyContent").val().trim();
// 로그인이 되어있지 않은 경우 == loginMemberId 전역 변수에 저장된 값이 "" 일 경우
if(loginMemberId == ""){
alert("로그인 후 이용해 주세요.");
} else { // 로그인이 되어있는 경우
// 댓글 내용이 작성되어있는지 확인
if(replyContent.length == 0){
alert("댓글 작성 후 클릭해주세요.");
} else { // 로그인도 되어있고, 댓글도 작성되어있는 경우
// 회원 번호를 얻어와서 변수에 저장
var replyWriter = "${loginMember.memberNo}";
$.ajax({
url : "${contextPath}/reply/insertReply.do",
data : {"replyWriter" : replyWriter,
"replyContent" : replyContent,
"parentBoardNo" : parentBoardNo},
type : "post",
success : function(result){
if(result >0){ // 댓글 삽입 성공 시
// 댓글 작성 내용 삭제
$("#replyContent").val("");
// 성공 메세지 출력
swal({"icon" : "success", "title" : "댓글 등록 성공"});
// 댓글 목록을 다시 조회 -> 새로 삽입한 댓글도 조회하여 화면에 출력
selectReplyList();
}
},
error : function(){
console.log("댓글 등록 실패");
}
});
}
}
});
</script>
- ReplyController.java
// 댓글 삽입 Controller ******************************************************
else if(command.equals("/insertReply.do")) {
// 오라클에서 숫자로 이루어진 문자열은 자동으로 숫자로 변환되는 특징을 사용할 예정
String replyWriter = request.getParameter("replyWriter");
int parentBoardNo = Integer.parseInt(request.getParameter("parentBoardNo"));
String replyContent = request.getParameter("replyContent");
Reply reply = new Reply();
reply.setMemberId(replyWriter);
reply.setReplyContent(replyContent);
reply.setParentBoardNo(parentBoardNo);
int result = service.insertReply(reply);
response.getWriter().print(result); // result 결과를 바로 응답페이지로 보내버림
// -> ajax!!!
}
- ReplyService.java
/** 댓글 삽입 Service
* @param reply
* @return result
* @throws Exception
*/
public int insertReply(Reply reply) throws Exception{
Connection conn = getConnection();
String replyContent = reply.getReplyContent();
// 크로스 사이트 스크립팅 방지 처리
replyContent = replaceParameter(replyContent);
// 개행문자 변환 처리
// ajax 통신 시 textarea의 개행문자가 \n 하나만 넘어옴 -> <br>
replyContent = replyContent.replace("\n", "<br>");
// 변경된 replyContent를 다시 reply에 세팅
reply.setReplyContent(replyContent);
int result = dao.insertReply(conn, reply);
// 트랜잭션 처리
if(result > 0) commit(conn);
else rollback(conn);
close(conn);
return result;
}
// 크로스 사이트 스크립트 방지 메소드
private String replaceParameter(String param) {
String result = param;
if(param != null) {
result = result.replaceAll("&", "&");
result = result.replaceAll("<", "<");
result = result.replaceAll(">", ">");
result = result.replaceAll("\"", """);
}
return result;
}
- reply-query.xml
<!-- 댓글 삽입 -->
<entry key="insertReply">
INSERT INTO REPLY VALUES(SEQ_RNO.NEXTVAL, ?, DEFAULT, DEFAULT, ?, ?)
</entry>
- ReplyDAO.java
/** 댓글 삽입 DAO
* @param conn
* @param reply
* @return result
* @throws Exception
*/
public int insertReply(Connection conn, Reply reply) throws Exception {
int result = 0;
String query = prop.getProperty("insertReply");
try {
pstmt = conn.prepareStatement(query);
pstmt.setString(1, reply.getReplyContent());
pstmt.setString(2, reply.getMemberId());
pstmt.setInt(3, reply.getParentBoardNo());
result = pstmt.executeUpdate();
} finally {
close(pstmt);
}
return result;
}
댓글 수정
댓글 수정 폼과 기능을 만드는데 복붙으로 수업해서 나중에 더 자세히 봐야겠다.
- 수정 성공 시
-
취소 버튼 클릭 시
- reply.jsp
<script>
var beforeReplyRow;
// 댓글 수정 폼 출력 함수
function showUpdateReply(replyNo, el){
//console.log($(".replyUpdateContent").length);
// 이미 열려있는 댓글 수정 창이 있을 경우 닫아주기
if($(".replyUpdateContent").length > 0){
$(".replyUpdateContent").eq(0).parent().html(beforeReplyRow);
}
// 댓글 수정화면 출력 전 요소를 저장해둠.
beforeReplyRow = $(el).parent().parent().html();
//console.log(beforeReplyRow);
// 작성되어있던 내용(수정 전 댓글 내용)
var beforeContent = $(el).parent().prev().html();
// 이전 댓글 내용의 크로스사이트 스크립트 처리 해제, 개행문자 변경
// -> 자바스크립트에는 replaceAll() 메소드가 없으므로 정규 표현식을 이용하여 변경
beforeContent = beforeContent.replace(/&/g, "&");
beforeContent = beforeContent.replace(/</g, "<");
beforeContent = beforeContent.replace(/>/g, ">");
beforeContent = beforeContent.replace(/"/g, "\"");
beforeContent = beforeContent.replace(/<br>/g, "\n");
//console.log(beforeContent)
// 기존 댓글 영역을 삭제하고 textarea를 추가
$(el).parent().prev().remove();
var textarea = $("<textarea>").addClass("replyUpdateContent").attr("rows", "3").val(beforeContent);
$(el).parent().before(textarea);
//console.log(replyBtnArea);
// 수정 버튼
var updateReply = $("<button>").addClass("btn btn-primary btn-sm ml-1 mb-4").text("댓글 수정").attr("onclick", "updateReply(" + replyNo + ", this)");
// 취소 버튼
var cancelBtn = $("<button>").addClass("btn btn-primary btn-sm ml-1 mb-4").text("취소").attr("onclick", "updateCancel(this)");
var replyBtnArea = $(el).parent();
$(replyBtnArea).empty();
$(replyBtnArea).append(updateReply);
$(replyBtnArea).append(cancelBtn);
}
//-----------------------------------------------------------------------------------------
// 댓글 수정 함수
function updateReply(replyNo, el){
// 수정된 댓글 내용
var replyContent = $(el).parent().prev().val();
$.ajax({
url : "${contextPath}/reply/updateReply.do",
type : "post",
data : {"replyNo" : replyNo, "replyContent" : replyContent},
success : function(result){
if(result > 0){
selectReplyList(parentBoardNo);
swal({"icon" : "success" , "title" : "댓글 수정 성공"});
}
}, error : function(){
console.log("댓글 수정 실패");
}
});
}
// 댓글 수정 취소 시 원래대로 돌아가는 함수
function updateCancel(el){
//console.log(beforeReplyRow);
$(el).parent().parent().html(beforeReplyRow);
}
</script>
- ReplyController.java
// 댓글 수정 Controller ******************************************************
else if(command.equals("/updateReply.do")) {
//파라미터(댓글 번호, 댓글 내용) 얻어오기
int replyNo = Integer.parseInt(request.getParameter("replyNo"));
String replyContent = request.getParameter("replyContent");
Reply reply = new Reply();
reply.setReplyNo(replyNo);
reply.setReplyContent(replyContent);
int result = service.updateReply(reply);
response.getWriter().print(result);
}
- ReplayService.java
/** 댓글 수정 Service
* @param reply
* @return result
* @throws Exception
*/
public int updateReply(Reply reply) throws Exception{
Connection conn = getConnection();
String replyContent = reply.getReplyContent();
// 크로스 사이트 스크립팅 방지 처리
replyContent = replaceParameter(replyContent);
// 개행문자 변환 처리
// ajax 통신 시 textarea의 개행문자가 \n 하나만 넘어옴 -> <br>
replyContent = replyContent.replace("\n", "<br>");
// 변경된 replyContent를 다시 reply에 세팅
reply.setReplyContent(replyContent);
int result = dao.updateReply(conn, reply);
if(result > 0) commit(conn);
else rollback(conn);
close(conn);
return result;
}
- reply-query.xml
<!-- 댓글 수정 -->
<entry key="updateReply">
UPDATE REPLY SET
REPLY_CONTENT = ?
WHERE REPLY_NO = ?
</entry>
- ReplyDAO.java
/** 댓글 수정 DAO
* @param conn
* @param reply
* @return result
* @throws Exception
*/
public int updateReply(Connection conn, Reply reply) throws Exception{
int result = 0;
String query = prop.getProperty("updateReply");
try {
pstmt = conn.prepareStatement(query);
pstmt.setString(1, reply.getReplyContent());
pstmt.setInt(2, reply.getReplyNo());
result = pstmt.executeUpdate();
} finally {
close(pstmt);
}
return result;
}
댓글 삭제
- 댓글 삭제 성공 시
-
DB 상태 변경
- reply.jsp
<script>
//댓글 삭제 함수
function deleteReply(replyNo){
if(confirm("정말로 삭제하시겠습니까?")){
var url = "${contextPath}/reply/deleteReply.do";
$.ajax({
url : url,
data : {"replyNo" : replyNo},
success : function(result){
if(result > 0){
selectReplyList(parentBoardNo);
swal({"icon" : "success" , "title" : "댓글 삭제 성공"});
}
}, error : function(){
console.log("ajax 통신 실패");
}
});
}
}
</script>
- ReplyController.java
// 댓글 삭제 Controller *******************************
else if(command.equals("/deleteReply.do")) {
int replyNo = Integer.parseInt(request.getParameter("replyNo"));
int result = service.updateReplyStatus(replyNo);
response.getWriter().print(result);
}
- ReplayService.java
/** 댓글 상태 변경 Service
* @param replyNo
* @return result
* @throws Exception
*/
public int updateReplyStatus(int replyNo) throws Exception {
Connection conn = getConnection();
int result = dao.updateReplyStatus(conn, replyNo);
if(result > 0) commit(conn);
else rollback(conn);
close(conn);
return result;
}
- reply-query.xml
<!-- 댓글 삭제 -->
<entry key="deleteBoard">
UPDATE BOARD SET
BOARD_STATUS = 'N'
WHERE BOARD_NO = ?
</entry>
- ReplyDAO.java
/** 댓글 상태 변경 DAO
* @param conn
* @param replyNo
* @return result
* @throws Exception
*/
public int updateReplyStatus(Connection conn, int replyNo) throws Exception {
int result = 0;
String query = prop.getProperty("updateReplyStatus");
try {
pstmt = conn.prepareStatement(query);
pstmt.setInt(1, replyNo);
result = pstmt.executeUpdate();
} finally {
close(pstmt);
}
return result;
}
댓글남기기