1. 개요
■ 지난 포스팅 : [JSP개발] 게시판 - 댓글 답변 구현
댓글을 삭제하는 기능을 구현할 것이다. 댓글 삭제의 경우 게시글이나 방명록과 마찬가지로 삭제 시 하위에 있는 답글도 모두 삭제되도록 한다. 그리고 게시글이 삭제되면 댓글도 모두 삭제되게 하기 위해 댓글 테이블에 제약조건을 추가해준다.
■ JSP
BoardDetailForm.jsp : 상세보기 화면에는 댓글 삭제와 관련된 코드를 추가한다.
■ Java
CommentDeleteAction.java : 댓글 삭제 Action이다.
CommentDAO.java : DAO에는 댓글을 삭제하는 메서드를 추가한다.
■ Table
BOARD_COMMENT : 게시글 삭제 시 댓글도 같이 삭제되도록 하기 위해 제약조건을 추가한다.
2. 소스 코드
■ BOARD_COMMENT 테이블
댓글 테이블은 게시판 테이블의 PK를 FK로 가지고 있다. 현재는 댓글이 있는 게시글을 삭제하려고 하면 FK를 사용하는 테이블이 있다며 삭제가 되지 않는다. 그래서 게시글 삭제 시 게시글에 달린 댓글도 모두 삭제되게 하기 위해 제약조건을 수정해야 한다.
1 2 3 4 5 6 7 8 9 10 11 12 | -- 댓글 테이블에 기존 제약조건 삭제 ALTER TABLE BOARD_COMMENT DROP CONSTRAINT "COMMENT_BOARD" -- 제약조건 ON DELETE CASCADE 추가 ALTER TABLE BOARD_COMMENT ADD CONSTRAINT COMMENT_BOARD FOREIGN KEY(COMMENT_BOARD) REFERENCES MEMBER_BOARD(BOARD_NUM) ON DELETE CASCADE ; -- ON DELETE CASCADE : 참조 테이블의 튜플이 삭제되면 기본 테이블의 관련 튜블도 같이 삭제 -- 즉 게시글이 삭제되면 게시물에 달려있는 댓글들도 모두 삭제됨, -- 이는 게시물의 글번호를 댓글이 FK로 가지고 있기 때문 | cs |
■ BoardDetailForm.jsp
[삭제] 클릭 시 cmDeleteOpen( )을 호출하도록 한다. 이때 댓글의 글 번호를 전달한다.
스크립트에는 삭제창을 여는 함수를 추가한다. 삭제창은 별도로 화면을 만들지 않고 confirm( )을 이용한다. confirm( )은 확인, 취소 버튼이 있는 alert 이다. 확인을 누르면 true를 취소를 누르면 false를 리턴한다.
확인을 눌렀을 경우 deleteCmt( )를 호출한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 | <%@ page language="java" contentType="text/html; charset=EUC-KR" pageEncoding="EUC-KR"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <html> <head> <title>글 상세보기</title> <style type="text/css"> #wrap { width: 800px; margin: 0 auto 0 auto; } #detailBoard{ text-align :center; } #title{ height : 16; font-family :'돋움'; font-size : 12; text-align :center; background-color: #F7F7F7; } #btn{ font-family :'돋움'; font-size : 14; text-align :center; } </style> <script type="text/javascript"> function changeView(value) { if(value == 0) location.href='BoardListAction.bo?page=${pageNum}'; else if(value == 1) location.href='BoardReplyFormAction.bo?num=${board.board_num}&page=${pageNum}'; } function doAction(value) { if(value == 0) // 수정 location.href="BoardUpdateFormAction.bo?num=${board.board_num}&page=${pageNum}"; else if(value == 1) // 삭제 location.href="BoardDeleteAction.bo?num=${board.board_num}"; } var httpRequest = null; // httpRequest 객체 생성 function getXMLHttpRequest(){ var httpRequest = null; if(window.ActiveXObject){ try{ httpRequest = new ActiveXObject("Msxml2.XMLHTTP"); } catch(e) { try{ httpRequest = new ActiveXObject("Microsoft.XMLHTTP"); } catch (e2) { httpRequest = null; } } } else if(window.XMLHttpRequest){ httpRequest = new window.XMLHttpRequest(); } return httpRequest; } // 댓글 등록 function writeCmt() { var form = document.getElementById("writeCommentForm"); var board = form.comment_board.value var id = form.comment_id.value var content = form.comment_content.value; if(!content) { alert("내용을 입력하세요."); return false; } else { var param="comment_board="+board+"&comment_id="+id+"&comment_content="+content; httpRequest = getXMLHttpRequest(); httpRequest.onreadystatechange = checkFunc; httpRequest.open("POST", "CommentWriteAction.co", true); httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=EUC-KR'); httpRequest.send(param); } } function checkFunc(){ if(httpRequest.readyState == 4){ // 결과값을 가져온다. var resultText = httpRequest.responseText; if(resultText == 1){ document.location.reload(); // 상세보기 창 새로고침 } } } // 댓글 답변창 function cmReplyOpen(comment_num){ var userId = '${sessionScope.sessionID}'; if(userId == "" || userId == null){ alert("로그인후 사용가능합니다."); return false; } else{ // 댓글 답변창 open window.name = "parentForm"; window.open("CommentReplyFormAction.co?num="+comment_num, "replyForm", "width=570, height=350, resizable = no, scrollbars = no"); } } // 댓글 삭제창 function cmDeleteOpen(comment_num){ var msg = confirm("댓글을 삭제합니다."); if(msg == true){ // 확인을 누를경우 deleteCmt(comment_num); } else{ return false; // 삭제취소 } } // 댓글 삭제 function deleteCmt(comment_num) { var param="comment_num="+comment_num; httpRequest = getXMLHttpRequest(); httpRequest.onreadystatechange = checkFunc; httpRequest.open("POST", "CommentDeleteAction.co", true); httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=EUC-KR'); httpRequest.send(param); } </script> </head> <body> <div id="wrap"> <br><br> <div id="board"> <table id="detailBoard" width="800" border="3" bordercolor="lightgray"> <tr> <td id="title">작성일</td> <td>${board.board_date}</td> </tr> <tr> <td id="title">작성자</td> <td>${board.board_id}</td> </tr> <tr> <td id="title"> 제 목 </td> <td> ${board.board_subject} </td> </tr> <tr> <td id="title"> 내 용 </td> <td> ${board.board_content} </td> </tr> <tr> <td id="title"> 첨부파일 </td> <td> <a href='FileDownloadAction.bo?file_name=${board.board_file}'>${board.board_file}</a> </td> </tr> <tr align="center" valign="middle"> <td colspan="5"> <c:if test="${sessionScope.sessionID !=null}"> <c:if test="${sessionScope.sessionID == board.board_id}"> <input type="button" value="수정" onclick="doAction(0)"> <input type="button" value="삭제" onclick="doAction(1)"> </c:if> <input type="button" value="답글" onclick="changeView(1)" > </c:if> <input type="button" value="목록" onclick="changeView(0)"> </td> <!-- javascript:location.href='BoardListAction.bo?page=${pageNum}' --> </tr> </table> </div> <br><br> <!-- 댓글 부분 --> <div id="comment"> <table border="1" bordercolor="lightgray"> <!-- 댓글 목록 --> <c:if test="${requestScope.commentList != null}"> <c:forEach var="comment" items="${requestScope.commentList}"> <tr> <!-- 아이디, 작성날짜 --> <td width="150"> <div> <c:if test="${comment.comment_level > 1}"> <!-- 답변글일경우 아이디 앞에 공백을 준다. --> <img src="img/reply_icon.gif"> </c:if> ${comment.comment_id}<br> <font size="2" color="lightgray">${comment.comment_date}</font> </div> </td> <!-- 본문내용 --> <td width="550"> <div class="text_wrapper"> ${comment.comment_content} </div> </td> <!-- 버튼 --> <td width="100"> <div id="btn" style="text-align:center;"> <a href="#" onclick="cmReplyOpen(${comment.comment_num})">[답변]</a><br> <!-- 댓글 작성자만 수정, 삭제 가능하도록 --> <c:if test="${comment.comment_id == sessionScope.sessionID}"> <a href="#">[수정]</a><br> <a href="#" onclick="cmDeleteOpen(${comment.comment_num})">[삭제]</a> </c:if> </div> </td> </tr> </c:forEach> </c:if> <!-- 로그인 했을 경우만 댓글 작성가능 --> <c:if test="${sessionScope.sessionID !=null}"> <tr bgcolor="#F5F5F5"> <form id="writeCommentForm"> <input type="hidden" name="comment_board" value="${board.board_num}"> <input type="hidden" name="comment_id" value="${sessionScope.sessionID}"> <!-- 아이디--> <td width="150"> <div> ${sessionScope.sessionID} </div> </td> <!-- 본문 작성--> <td width="550"> <div> <textarea name="comment_content" rows="4" cols="70" ></textarea> </div> </td> <!-- 댓글 등록 버튼 --> <td width="100"> <div id="btn" style="text-align:center;"> <p><a href="#" onclick="writeCmt()">[댓글등록]</a></p> </div> </td> </form> </tr> </c:if> </table> </div> </div> </body> </html> | cs |
■ CommentDeleteAction.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | package jsp.board.comment.action; import java.io.PrintWriter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import jsp.board.comment.model.CommentDAO; import jsp.common.action.Action; import jsp.common.action.ActionForward; public class CommentDeleteAction implements Action { @Override public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception { int comment_num = Integer.parseInt(request.getParameter("comment_num")); CommentDAO dao = CommentDAO.getInstance(); boolean result = dao.deleteComment(comment_num); response.setContentType("text/html;charset=euc-kr"); PrintWriter out = response.getWriter(); // 정상적으로 댓글을 삭제했을경우 1을 전달한다. if(result) out.println("1"); out.close(); return null; } } | cs |
■ CommentDAO.java
- 202~207줄 : 삭제하려는 댓글 글번호(comment_num)부터 계층을 이루는 모든 댓글을 검색하여 같이 삭제하도록 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | package jsp.board.comment.model; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import jsp.common.util.DBConnection; public class CommentDAO { private Connection conn; private PreparedStatement pstmt; private ResultSet rs; private static CommentDAO instance; private CommentDAO(){} public static CommentDAO getInstance(){ if(instance==null) instance=new CommentDAO(); return instance; } // 시퀀스를 가져온다. public int getSeq() { int result = 1; try { conn = DBConnection.getConnection(); // 시퀀스 값을 가져온다. (DUAL : 시퀀스 값을 가져오기위한 임시 테이블) StringBuffer sql = new StringBuffer(); sql.append("SELECT COMMENT_SEQ.NEXTVAL FROM DUAL"); pstmt = conn.prepareStatement(sql.toString()); rs = pstmt.executeQuery(); // 쿼리 실행 if (rs.next()) result = rs.getInt(1); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } close(); return result; } // end getSeq // 댓글 등록 public boolean insertComment(CommentBean comment) { boolean result = false; try { conn = DBConnection.getConnection(); // 자동 커밋을 false로 한다. conn.setAutoCommit(false); StringBuffer sql = new StringBuffer(); sql.append("INSERT INTO BOARD_COMMENT"); sql.append(" (COMMENT_NUM, COMMENT_BOARD, COMMENT_ID, COMMENT_DATE"); sql.append(" , COMMENT_PARENT, COMMENT_CONTENT)"); sql.append(" VALUES(?,?,?,sysdate,?,?)"); pstmt = conn.prepareStatement(sql.toString()); pstmt.setInt(1, comment.getComment_num()); pstmt.setInt(2, comment.getComment_board()); pstmt.setString(3, comment.getComment_id()); pstmt.setInt(4, comment.getComment_parent()); pstmt.setString(5, comment.getComment_content()); int flag = pstmt.executeUpdate(); if(flag > 0){ result = true; conn.commit(); // 완료시 커밋 } } catch (Exception e) { try { conn.rollback(); // 오류시 롤백 } catch (SQLException sqle) { sqle.printStackTrace(); } e.printStackTrace(); throw new RuntimeException(e.getMessage()); } close(); return result; } // end insertComment(); // 댓글 목록 가져오기 public ArrayList<CommentBean> getCommentList(int boardNum) { ArrayList<CommentBean> list = new ArrayList<CommentBean>(); try { conn = DBConnection.getConnection(); /* 댓글의 페이지 처리를 하고싶다면 이 쿼리를 사용하면 된다. * SELECT * FROM * (SELECT ROWNUM AS rnum, * data.* * FROM * (SELECT LEVEL, * COMMENT_NUM, * COMMENT_BOARD, * COMMENT_ID, * COMMENT_DATE, * COMMENT_PARENT, * COMMENT_CONTENT * FROM BOARD_COMMENT * WHERE COMMENT_BOARD = ? * START WITH COMMENT_PARENT = 0 * CONNECT BY PRIOR COMMENT_NUM = COMMENT_PARENT) * data) * WHERE rnum>=? and rnum<=? ; */ StringBuffer sql = new StringBuffer(); sql.append(" SELECT LEVEL, COMMENT_NUM, COMMENT_BOARD,"); sql.append(" COMMENT_ID, COMMENT_DATE,"); sql.append(" COMMENT_PARENT, COMMENT_CONTENT"); sql.append(" FROM BOARD_COMMENT"); sql.append(" WHERE COMMENT_BOARD = ?"); sql.append(" START WITH COMMENT_PARENT = 0"); sql.append(" CONNECT BY PRIOR COMMENT_NUM = COMMENT_PARENT"); pstmt = conn.prepareStatement(sql.toString()); pstmt.setInt(1, boardNum); rs = pstmt.executeQuery(); while(rs.next()) { CommentBean comment = new CommentBean(); comment.setComment_level(rs.getInt("LEVEL")); comment.setComment_num(rs.getInt("COMMENT_NUM")); comment.setComment_board(rs.getInt("COMMENT_BOARD")); comment.setComment_id(rs.getString("COMMENT_ID")); comment.setComment_date(rs.getDate("COMMENT_DATE")); comment.setComment_parent(rs.getInt("COMMENT_PARENT")); comment.setComment_content(rs.getString("COMMENT_CONTENT")); list.add(comment); } } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e.getMessage()); } close(); return list; } // end getCommentList // 댓글 1개의 정보를 가져온다. public CommentBean getComment(int comment_num) { CommentBean comment = null; try { conn = DBConnection.getConnection(); StringBuffer sql = new StringBuffer(); sql.append("SELECT * FROM BOARD_COMMENT WHERE COMMENT_NUM = ?"); pstmt = conn.prepareStatement(sql.toString()); pstmt.setInt(1, comment_num); rs = pstmt.executeQuery(); while(rs.next()) { comment = new CommentBean(); comment.setComment_num(rs.getInt("COMMENT_NUM")); comment.setComment_board(rs.getInt("COMMENT_BOARD")); comment.setComment_id(rs.getString("COMMENT_ID")); comment.setComment_date(rs.getDate("COMMENT_DATE")); comment.setComment_parent(rs.getInt("COMMENT_PARENT")); comment.setComment_content(rs.getString("COMMENT_CONTENT")); } } catch (Exception e) { throw new RuntimeException(e.getMessage()); } close(); return comment; } // end getComment // 댓글 삭제 public boolean deleteComment(int comment_num) { boolean result = false; try { conn = DBConnection.getConnection(); conn.setAutoCommit(false); // 자동 커밋을 false로 한다. StringBuffer sql = new StringBuffer(); sql.append("DELETE FROM BOARD_COMMENT"); sql.append(" WHERE COMMENT_NUM IN"); sql.append(" (SELECT COMMENT_NUM"); sql.append(" FROM BOARD_COMMENT"); sql.append(" START WITH COMMENT_NUM = ?"); sql.append(" CONNECT BY PRIOR COMMENT_NUM = COMMENT_PARENT) "); pstmt = conn.prepareStatement(sql.toString()); pstmt.setInt(1, comment_num); int flag = pstmt.executeUpdate(); if(flag > 0){ result = true; conn.commit(); // 완료시 커밋 } } catch (Exception e) { try { conn.rollback(); // 오류시 롤백 } catch (SQLException sqle) { sqle.printStackTrace(); } throw new RuntimeException(e.getMessage()); } close(); return result; } // end deleteComment // DB 자원해제 private void close() { try { if ( pstmt != null ){ pstmt.close(); pstmt=null; } if ( conn != null ){ conn.close(); conn=null; } } catch (Exception e) { throw new RuntimeException(e.getMessage()); } } // end close() } | cs |
■ CommentCommand.properties
댓글 삭제에 대한 명령어를 추가한다.
1 2 3 4 5 6 7 | # Form Change CommentReplyFormAction.co=jsp.board.comment.action.CommentReplyFormAction # Action CommentWriteAction.co=jsp.board.comment.action.CommentWriteAction CommentReplyAction.co=jsp.board.comment.action.CommentReplyAction CommentDeleteAction.co=jsp.board.comment.action.CommentDeleteAction | cs |
3. 실행 결과
------로 적혀있는 댓글을 삭제할 것이다.
DB를 보면 테이블에 댓글이 저장되어 있는 것을 볼 수 있다.
[삭제] 클릭 시 창이 뜬다. 여기서 확인을 누른다.
위에서 본 -------- 댓글과 댓글의 답글 모두 삭제되었다.
테이블을 보면 해당 글이 모두 삭제된 것을 확인할 수 있다.
그러면 [삭제테스트 게시글]이라는 글을 삭제해보자.
게시글을 삭제하면 거기에 달려있던 댓글인 댓글 [삭제 테스트]와 [삭제 테스트 답글] 모두 같이 삭제된 것을 볼 수 있다.
4. 소스코드 다운로드 (war 파일)
'코딩 > JSP' 카테고리의 다른 글
[JSP개발] 회원가입 - 아이디 중복체크 구현 (19) | 2017.01.06 |
---|---|
[JSP개발] 게시판 - 댓글 수정 (0) | 2017.01.05 |
[JSP개발] 게시판 - 댓글 답변 구현 (4) | 2017.01.03 |
[JSP개발] 게시판 - 댓글 작성 및 댓글 목록 (19) | 2017.01.02 |
[JSP개발] 게시판 - 방명록 수정 (15) | 2017.01.01 |