1. 개요
■ 지난 포스팅 : [JSP개발] 게시판 - 댓글 작성 및 댓글 목록
댓글 구현 후 댓글에 답변을 작성하는 것을 구현할 것이다. 답변의 경우 로그인한 사용자만 작성이 가능하도록 한다.
■ JSP
BoardDetailForm.jsp : 게시글 상세보기에는 댓글의 답변을 작성하는 창을 띄우는 코드를 추가한다.
CommentReplyFrom.jsp : 답변 작성 화면이다.
■ Java
CommentReplyFormAction.java : 댓글 답변 작성 화면을 띄우는 Action이다.
CommentReplyAction.java : 댓글 답변을 처리하는 Action 이다.
CommentDAO.java : 댓글 1개의 정보를 가져오는 메서드를 추가한다.
2. 소스 코드
■ BoardDetailForm.jsp
[답변] 클릭 시 cmReplyOpen( )을 호출하도록 한다. 이때 댓글의 글번호를 전달한다. 글 번호는 답글의 부모글 정보를 가져오는데 사용된다.
답변 글일 경우 공백과 이미지를 추가해준다.
194~197줄 : level 값은 1부터 시작된다. 그냥 댓글의 경우 level이 1이고 답글의 경우 1보다 큰 값을 가진다.
함수 cmReplyOpen( )는 답변 화면을 open한다.
112~116줄 : 답변은 로그인한 사용자만 작성 가능하다. 세션에 아이디가 없을 경우 경고 창을 띄운다.
120줄 : 부모 창의 이름을 지정한다. 여기서 부모창이란 상세보기 화면(BoardDetailForm)이다.
121~122줄 : open에는 URL, 자식창이름, 크기 및 기타 옵션 이렇게 3가지를 지정한다.
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 | <%@ 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"); } } </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="#">[삭제]</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 |
■ CommentReplyFormAction.java
CommentReplyFormAction은 댓글 글 번호를 이용해서 부모 댓글의 정보를 가져오고 그것을 답변 화면으로 전달한다.
20줄 : BoardDetailForm에서 전달받은 댓글의 글 번호를 가져온다.
22~23줄 : 글 번호를 이용하여 부모 댓글의 정보를 가져온다.
26줄 : 가져온 댓글 정보를 request에 세팅한다.
29줄 : 다음 화면으로 답변 화면(CommentReplyFrom)을 세팅한다.
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 | package jsp.board.comment.action; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import jsp.board.comment.model.CommentBean; import jsp.board.comment.model.CommentDAO; import jsp.common.action.Action; import jsp.common.action.ActionForward; public class CommentReplyFormAction implements Action { @Override public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception { ActionForward forward = new ActionForward(); // 부모글의 글번호를 가져온다. int comment_num = Integer.parseInt(request.getParameter("num")); CommentDAO dao = CommentDAO.getInstance(); CommentBean comment = dao.getComment(comment_num); // 댓글 정보를 request에 세팅한다. request.setAttribute("comment", comment); forward.setRedirect(false); forward.setNextPath("board/comment/CommentReplyFrom.jsp"); return forward; } } | cs |
■ CommentReplyFrom.jsp
댓글 답변 작성 부분이다. 간단하게 답변을 작성할 <textarea>와 2개의 버튼을 만들어준다.
답변 화면의 스크립트를 보자. 여기에서도 Ajax를 사용하였다. getXMLHttpRequest( ) 함수는 XMLHttpRequest 객체를 생성하는 역할을 한다.
checkValue( )는 답변 내용의 입력 여부를 체크한다. 내용을 입력했다면 그것을 서버로 전송한다.
50~53줄 : 전달할 값들을 변수에 담는다. 전달할 값은 댓글 글번호, 게시글 글번호, 사용자 ID, 답변 내용이다.
55~58줄 : 내용이 입력되었는지 검사한다. 내용이 입력되지 않았다면 경고 창을 띄운다.
61줄 : 서버로 전달할 파라미터 값이다.
65줄 : XMLHttpRequest 상태 변화시 호출될 함수를 지정한다.
66줄 : 전송 방식, 전송할 경로, 동기/비동기를 지정한다.
68줄 : 서버로 파라미터를 전송한다.
서버로 전송 이후 호출되는 함수이다. 정상적으로 데이터를 전송하고, 결과를 받았을 경우 77~81줄이 실행된다.
79~81줄 : 서버에서 결과값 1을 받을 경우 부모창 - 게시글 상세보기 화면을 새로고침하고 현재 창을 닫는다.
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 | <%@ page language="java" contentType="text/html; charset=EUC-KR" pageEncoding="EUC-KR"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=EUC-KR"> <title> 댓글 답변 </title> <style type="text/css"> #wrap { width: 550px; margin: 0 auto 0 auto; text-align :center; } #commentReplyForm{ text-align :center; } </style> <script type="text/javascript"> 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 checkValue() { var form = document.forms[0]; // 전송할 값을 변수에 담는다. var comment_num = "${comment.comment_num}"; var comment_board = "${comment.comment_board}"; var comment_id = "${sessionScope.sessionID}"; var comment_content = form.comment_content.value if(!comment_content) { alert("내용을 입력하세요"); return false; } else{ var param="comment_num="+comment_num+"&comment_board="+comment_board +"&comment_id="+comment_id+"&comment_content="+comment_content; httpRequest = getXMLHttpRequest(); httpRequest.onreadystatechange = checkFunc; httpRequest.open("POST", "CommentReplyAction.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){ if (opener != null) { // 부모창 새로고침 window.opener.document.location.reload(); opener.replyForm = null; self.close(); } } } } </script> </head> <body> <div id="wrap"> <br> <b><font size="5" color="gray">댓글 답변</font></b> <hr size="1" width="550"> <br> <div id="commentReplyForm"> <form name="replyInfo" target="parentForm"> <textarea rows="7" cols="70" name="comment_content"></textarea> <br><br> <input type="button" value="등록" onclick="checkValue()"> <input type="button" value="창닫기" onclick="window.close()"> </form> </div> </div> </body> </html> | cs |
■ CommentReplyAction.java
파라미터값을 가져와 변수에 담는다.
CommentBean에 변수를 세팅하고 그것을 DAO로 전달하여 저장한다. 그리고 화면으로 결과값을 전송한다.
28~32줄 : CommentBean에 값을 세팅한다. 글 번호는 시퀀스를 가져와서 사용하고, 부모 글 번호(comment_parent)는 부모 댓글의 글 번호를 세팅한다.
34줄 : 댓글을 저장한다.
40~41줄 : 정상적으로 저장되면 1을 아니면 0을 화면으로 전송한다.
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 | package jsp.board.comment.action; import java.io.PrintWriter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import jsp.board.comment.model.CommentBean; import jsp.board.comment.model.CommentDAO; import jsp.common.action.Action; import jsp.common.action.ActionForward; public class CommentReplyAction implements Action { @Override public ActionForward execute(HttpServletRequest request, HttpServletResponse response) throws Exception { // 파라미터를 가져온다. int comment_num = Integer.parseInt(request.getParameter("comment_num")); int comment_board = Integer.parseInt(request.getParameter("comment_board")); String comment_id = request.getParameter("comment_id"); String comment_content = request.getParameter("comment_content"); CommentDAO dao = CommentDAO.getInstance(); CommentBean comment = new CommentBean(); comment.setComment_num(dao.getSeq()); // 시퀀스를 가져와 세팅한다 comment.setComment_board(comment_board); comment.setComment_id(comment_id); comment.setComment_content(comment_content); comment.setComment_parent(comment_num); // 부모댓글의 글번호를 세팅 boolean result = dao.insertComment(comment); response.setContentType("text/html;charset=euc-kr"); PrintWriter out = response.getWriter(); // 정상적으로 댓글을 저장했을경우 1을 전달한다. if(result) out.println("1"); else out.println("0"); out.close(); return null; } } | cs |
■ CommentDAO.java
DAO에는 댓글 1개의 정보를 가져오는 메서드를 추가해준다.
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 | 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 boardInsert(); // 댓글 목록 가져오기 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 getGuestbook // 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 | # Form Change CommentReplyFormAction.co=jsp.board.comment.action.CommentReplyFormAction # Action CommentWriteAction.co=jsp.board.comment.action.CommentWriteAction CommentReplyAction.co=jsp.board.comment.action.CommentReplyAction | cs |
3. 실행 결과
로그인 후 댓글 옆에 보이는 [답변]을 클릭한다.
답변 창이 나타나면 답변 내용을 작성하고 등록을 누른다.
그러면 답변이 등록된 것을 확인할 수 있다.
4. 소스코드 다운로드 (war 파일)