일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- GRANT VIEW
- Java
- exception
- 환경설정
- 참조형변수
- 자바
- EnhancedFor
- 사용자예외클래스생성
- NestedFor
- 어윈 사용법
- 생성자오버로드
- 컬렉션프레임워크
- 제네릭
- oracle
- 한국건설관리시스템
- 컬렉션 타입
- 다형성
- 자동차수리시스템
- 정수형타입
- cursor문
- 대덕인재개발원
- 오라클
- 객체 비교
- 메소드오버로딩
- 예외미루기
- 예외처리
- abstract
- 집합_SET
- 인터페이스
- 추상메서드
- Today
- Total
거니의 velog
(10) 모델2 방식으로 효율적으로 개발하기 4_답변형 게시판 구현 7 본문
(6) 답글 쓰기 기능 구현
* 지금까지 게시판에서 글 목록을 보고, 새 글을 쓰고, 글 상세를 보고, 수정 및 삭제하는 기능까지 구현해 보았다. 얼추 게시판의 기능을 모두 갖춘 것 같다. 하지만 아직 빠진 것이 있다. 보통 쇼핑몰 게시판을 보면 댓글, 즉 답글을 쓸 수 있는 기능이 있다.
* 따라서 이번에는 게시판의 답글 쓰는 기능을 구현해 보자. 다음은 그 과정이다.
(1) 글 상세창(viewArticle.jsp)에서 답글쓰기를 클릭하면 요청명을 /board/replyForm.do로 하여 부모 글 번호(parentNO)를 컨트롤러로 전송한다.
(2) 답글 쓰기창(replyForm.jsp)에서 답변 글을 작성한 후 요청명을 /board/addReply.do로 하여 컨트롤러로 요청한다.
(3) 컨트롤러에서는 전송된 답글 정보를 게시판 테이블에 부모 글 번호와 함께 추가한다.
1. sec03.brd07 패키지를 만들고 관련된 클래스 파일을 복사해 넣은 후 board06 폴더를 만들고 JSP 파일을 추가한다.
2. 답글도 새 글이므로 답글 기능도 새 글 쓰기 기능과 비슷하다. 다른 점은 답글창 요청(/replyForm.do)시 미리 부모 글 번호를 parentNO 속성으로 세션(session)에 저장해 놓고, 답글을 작성한 후 등록을 요청(/addReply.do)하면 세션에서 parentNO를 가져와 테이블에 추가한다는 점이다.
package sec03.brd07;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.RequestDispatcher;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
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 javax.servlet.http.HttpSession;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FileUtils;
@WebServlet("/board/*")
public class BoardController extends HttpServlet {
private static String ARTICLE_IMAGE_REPO = "C:\\board\\article_image";
BoardService boardService;
ArticleVO articleVO;
public void init(ServletConfig config) throws ServletException {
boardService = new BoardService();
articleVO = new ArticleVO();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doHandle(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doHandle(request, response);
}
private void doHandle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String nextPage = "";
request.setCharacterEncoding("utf-8");
response.setContentType("text/html; charset=utf-8");
HttpSession session; // 답글에 대한 부모 글 번호를 저장하기 위해 세션을 사용한다.
String action = request.getPathInfo();
System.out.println("action:" + action);
try {
List<ArticleVO> articlesList = new ArrayList<ArticleVO>();
if (action == null) {
articlesList = boardService.listArticles();
request.setAttribute("articlesList", articlesList);
nextPage = "/board06/listArticles.jsp";
} else if (action.equals("/listArticles.do")) {
articlesList = boardService.listArticles();
request.setAttribute("articlesList", articlesList);
nextPage = "/board06/listArticles.jsp";
} else if (action.equals("/articleForm.do")) {
nextPage = "/board06/articleForm.jsp";
} else if (action.equals("/addArticle.do")) {
int articleNO = 0;
Map<String, String> articleMap = upload(request, response);
String title = articleMap.get("title");
String content = articleMap.get("content");
String imageFileName = articleMap.get("imageFileName");
articleVO.setParentNO(0);
articleVO.setId("hong");
articleVO.setTitle(title);
articleVO.setContent(content);
articleVO.setImageFileName(imageFileName);
articleNO = boardService.addArticle(articleVO);
if (imageFileName != null && imageFileName.length() != 0) {
File srcFile = new File(ARTICLE_IMAGE_REPO + "\\" + "temp" + "\\" + imageFileName);
File destDir = new File(ARTICLE_IMAGE_REPO + "\\" + articleNO);
destDir.mkdirs();
FileUtils.moveFileToDirectory(srcFile, destDir, true);
}
PrintWriter pw = response.getWriter();
pw.print("<script>" + " alert('새글을 추가했습니다.');" + " location.href='" + request.getContextPath()
+ "/board/listArticles.do';" + "</script>");
return;
} else if (action.equals("/viewArticle.do")) {
String articleNO = request.getParameter("articleNO");
articleVO = boardService.viewArticle(Integer.parseInt(articleNO));
request.setAttribute("article", articleVO);
nextPage = "/board06/viewArticle.jsp";
} else if (action.equals("/modArticle.do")) {
Map<String, String> articleMap = upload(request, response);
int articleNO = Integer.parseInt(articleMap.get("articleNO"));
articleVO.setArticleNO(articleNO);
String title = articleMap.get("title");
String content = articleMap.get("content");
String imageFileName = articleMap.get("imageFileName");
articleVO.setParentNO(0);
articleVO.setId("hong");
articleVO.setTitle(title);
articleVO.setContent(content);
articleVO.setImageFileName(imageFileName);
boardService.modArticle(articleVO);
if (imageFileName != null && imageFileName.length() != 0) {
String originalFileName = articleMap.get("originalFileName");
File srcFile = new File(ARTICLE_IMAGE_REPO + "\\" + "temp" + "\\" + imageFileName);
File destDir = new File(ARTICLE_IMAGE_REPO + "\\" + articleNO);
destDir.mkdirs();
FileUtils.moveFileToDirectory(srcFile, destDir, true);
;
File oldFile = new File(ARTICLE_IMAGE_REPO + "\\" + articleNO + "\\" + originalFileName);
oldFile.delete();
}
PrintWriter pw = response.getWriter();
pw.print("<script>" + " alert('글을 수정했습니다.');" + " location.href='" + request.getContextPath()
+ "/board/viewArticle.do?articleNO=" + articleNO + "';" + "</script>");
return;
} else if (action.equals("/removeArticle.do")) {
int articleNO = Integer.parseInt(request.getParameter("articleNO"));
List<Integer> articleNOList = boardService.removeArticle(articleNO);
for (int _articleNO : articleNOList) {
File imgDir = new File(ARTICLE_IMAGE_REPO + "\\" + _articleNO);
if (imgDir.exists()) {
FileUtils.deleteDirectory(imgDir);
}
}
PrintWriter pw = response.getWriter();
pw.print("<script>" + " alert('글을 삭제했습니다.');" + " location.href='" + request.getContextPath()
+ "/board/listArticles.do';" + "</script>");
return;
} else if (action.equals("/replyForm.do")) {
int parentNO = Integer.parseInt(request.getParameter("parentNO"));
session = request.getSession();
session.setAttribute("parentNO", parentNO); // 답글창 요청 시 미리 부모 글 번호를 parentNO 속성으로 세션에 저장한다.
nextPage = "/board06/replyForm.jsp";
} else if (action.equals("/addReply.do")) {
session = request.getSession();
int parentNO = (Integer) session.getAttribute("parentNO");
session.removeAttribute("parentNO"); // 답글 전송 시 세션에 저장된 parentNO를 가져온다.
Map<String, String> articleMap = upload(request, response);
String title = articleMap.get("title");
String content = articleMap.get("content");
String imageFileName = articleMap.get("imageFileName");
articleVO.setParentNO(parentNO); // 답글의 부모 글 번호를 설정한다.
articleVO.setId("lee"); // 답글 작성자 ID를 lee로 설정한다.
articleVO.setTitle(title);
articleVO.setContent(content);
articleVO.setImageFileName(imageFileName);
int articleNO = boardService.addReply(articleVO); // 답글을 테이블에 추가한다.
if (imageFileName != null && imageFileName.length() != 0) {
File srcFile = new File(ARTICLE_IMAGE_REPO + "\\" + "temp" + "\\" + imageFileName);
File destDir = new File(ARTICLE_IMAGE_REPO + "\\" + articleNO);
destDir.mkdirs();
FileUtils.moveFileToDirectory(srcFile, destDir, true);
} // 답글에 첨부한 이미지를 temp 폴더에 답글 번호 폴더로 이동한다.
PrintWriter pw = response.getWriter();
pw.print("<script>" + " alert('답글을 추가했습니다.');" + " location.href='" + request.getContextPath()
+ "/board/viewArticle.do?articleNO="+articleNO+"';" + "</script>");
return;
}else {
nextPage = "/board06/listArticles.jsp";
}
RequestDispatcher dispatch = request.getRequestDispatcher(nextPage);
dispatch.forward(request, response);
} catch (Exception e) {
e.printStackTrace();
}
}
private Map<String, String> upload(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Map<String, String> articleMap = new HashMap<String, String>();
String encoding = "utf-8";
File currentDirPath = new File(ARTICLE_IMAGE_REPO);
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setRepository(currentDirPath);
factory.setSizeThreshold(1024 * 1024);
ServletFileUpload upload = new ServletFileUpload(factory);
try {
List items = upload.parseRequest(request);
for (int i = 0; i < items.size(); i++) {
FileItem fileItem = (FileItem) items.get(i);
if (fileItem.isFormField()) {
System.out.println(fileItem.getFieldName() + "=" + fileItem.getString(encoding));
articleMap.put(fileItem.getFieldName(), fileItem.getString(encoding));
} else {
System.out.println("파라미터명:" + fileItem.getFieldName());
//System.out.println("파일명:" + fileItem.getName());
System.out.println("파일크기:" + fileItem.getSize() + "bytes");
//articleMap.put(fileItem.getFieldName(), fileItem.getName());
if (fileItem.getSize() > 0) {
int idx = fileItem.getName().lastIndexOf("\\");
if (idx == -1) {
idx = fileItem.getName().lastIndexOf("/");
}
String fileName = fileItem.getName().substring(idx + 1);
System.out.println("파일명:" + fileName);
articleMap.put(fileItem.getFieldName(), fileName); //익스플로러에서 업로드 파일의 경로 제거 후 map에 파일명 저장
File uploadFile = new File(currentDirPath + "\\temp\\" + fileName);
fileItem.write(uploadFile);
} // end if
} // end if
} // end for
} catch (Exception e) {
e.printStackTrace();
}
return articleMap;
}
}
3. 답글 쓰기는 새 글 쓰기와 동일하게 BoardDAO의 insertNewArticle() 메서드를 이용한다. BoardService 클래스를 다음과 같이 수정한다.
package sec03.brd07;
import java.util.List;
public class BoardService {
BoardDAO boardDAO;
public BoardService() {
boardDAO = new BoardDAO();
}
public List<ArticleVO> listArticles() {
List<ArticleVO> articlesList = boardDAO.selectAllArticles();
return articlesList;
}
public int addArticle(ArticleVO article) {
return boardDAO.insertNewArticle(article);
}
public ArticleVO viewArticle(int articleNO) {
ArticleVO article = null;
article = boardDAO.selectArticle(articleNO);
return article;
}
public void modArticle(ArticleVO article) {
boardDAO.updateArticle(article);
}
public List<Integer> removeArticle(int articleNO) {
List<Integer> articleNOList = boardDAO.selectRemovedArticles(articleNO);
boardDAO.deleteArticle(articleNO);
return articleNOList;
}
public int addReply(ArticleVO article) {
return boardDAO.insertNewArticle(article); // 새 글 추가시 사용한 insertNewArticle() 메서드를 이용해 답글을 추가한다.
}
}
4. 글 상세창(viewArticle.jsp)에서 답글쓰기를 클릭하면 fn_reply_form() 함수를 호출하면서 글 번호와 요청명을 함께 전달한다. 그리고 다시 <form> 태그와 <input> 태그를 이용해 글 번호를 parentNO 속성으로 컨트롤러에 전달한다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"
isELIgnored="false" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%
request.setCharacterEncoding("UTF-8");
%>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>글보기</title>
<style>
#tr_btn_modify {
display: none;
}
</style>
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script type="text/javascript">
function backToList(obj) {
obj.action = "${contextPath}/board/listArticles.do";
obj.submit();
}
function fn_enable(obj) {
document.getElementById("i_title").disabled = false;
document.getElementById("i_content").disabled = false;
document.getElementById("i_imageFileName").disabled = false;
document.getElementById("tr_btn_modify").style.display = "block";
document.getElementById("tr_btn").style.display = "none";
}
function fn_modify_article(obj) {
obj.action = "${contextPath}/board/modArticle.do";
obj.submit();
}
function fn_remove_article(url, articleNO) {
var form = document.createElement("form");
form.setAttribute("method", "post");
form.setAttribute("action", url);
var articleNOInput = document.createElement("input");
articleNOInput.setAttribute("type", "hidden");
articleNOInput.setAttribute("name", "articleNO");
articleNOInput.setAttribute("value", articleNO);
form.appendChild(articleNOInput);
document.body.appendChild(form);
form.submit();
}
function fn_reply_form(url, parentNO) {
var form = document.createElement("form");
form.setAttribute("method", "post");
form.setAttribute("action", url); // 전달된 요청명을 <form> 태그의 action 속성 값에 설정한다.
var parentNOInput = document.createElement("input");
parentNOInput.setAttribute("type", "hidden");
parentNOInput.setAttribute("name", "parentNO");
parentNOInput.setAttribute("value", parentNO); // 함수 호출 시 전달된 articleNO 값을 <input> 태그를 이용해 컨트롤러에 전달한다.
form.appendChild(parentNOInput);
document.body.appendChild(form);
form.submit();
}
function readURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function(e) {
$('#preview').attr('src', e.target.result);
}
reader.readAsDataURL(input.files[0]);
}
}
</script>
</head>
<body>
<form name="frmArticle" method="post" action="${contextPath}" enctype="multipart/form-data">
<table border=0 align="center">
<tr>
<td width=150 align="center" bgcolor=#FF9933>
글번호
</td>
<td>
<input type="text" value="${article.articleNO }" disabled />
<input type="hidden" name="articleNO" value="${article.articleNO}" />
</td>
</tr>
<tr>
<td width="150" align="center" bgcolor="#FF9933">
작성자 아이디
</td>
<td>
<input type=text value="${article.id }" name="writer" disabled />
</td>
</tr>
<tr>
<td width="150" align="center" bgcolor="#FF9933">
제목
</td>
<td>
<input type=text value="${article.title }" name="title" id="i_title" disabled />
</td>
</tr>
<tr>
<td width="150" align="center" bgcolor="#FF9933">
내용
</td>
<td>
<textarea rows="20" cols="60" name="content" id="i_content" disabled />${article.content }</textarea>
</td>
</tr>
<c:if test="${not empty article.imageFileName && article.imageFileName!='null' }">
<tr>
<td width="150" align="center" bgcolor="#FF9933" rowspan="2">
이미지
</td>
<td>
<input type="hidden" name="originalFileName" value="${article.imageFileName }" />
<img src="${contextPath}/download.do?articleNO=${article.articleNO}&imageFileName=${article.imageFileName}" id="preview" /><br>
</td>
</tr>
<tr>
<td>
<input type="file" name="imageFileName " id="i_imageFileName" disabled onchange="readURL(this);" />
</td>
</tr>
</c:if>
<tr>
<td width="150" align="center" bgcolor="#FF9933">
등록일자
</td>
<td>
<input type=text value="<fmt:formatDate value=" ${article.writeDate}" />" disabled />
</td>
</tr>
<tr id="tr_btn_modify">
<td colspan="2" align="center">
<input type=button value="수정반영하기" onClick="fn_modify_article(frmArticle)">
<input type=button value="취소" onClick="backToList(frmArticle)">
</td>
</tr>
<tr id="tr_btn">
<td colspan="2" align="center">
<input type=button value="수정하기" onClick="fn_enable(this.form)">
<input type=button value="삭제하기" onClick="fn_remove_article('${contextPath}/board/removeArticle.do', ${article.articleNO})">
<input type=button value="리스트로 돌아가기" onClick="backToList(this.form)">
<input type=button value="답글쓰기" onClick="fn_reply_form('${contextPath}/board/replyForm.do', ${article.articleNO})"> <%-- 답글쓰기 클릭 시 fn_reply_form() 함수를 호출하면서 요청명과 글 번호를 전달한다. --%>
</td>
</tr>
</table>
</form>
</body>
</html>
5. 답글을 입력한 후 컨트롤러에 /board/addReply.do 로 요청하도록 repltForm.jsp를 다음과 같이 작성한다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"
isELIgnored="false" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
<%
request.setCharacterEncoding("UTF-8");
%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script type="text/javascript">
function backToList(obj) {
obj.action = "${contextPath}/board/listArticles.do";
obj.submit();
}
function readURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function(e) {
$('#preview').attr('src', e.target.result);
}
reader.readAsDataURL(input.files[0]);
}
}
</script>
<title>답글쓰기 페이지</title>
</head>
<body>
<h1 style="text-align:center">답글쓰기</h1> <%-- 답글 입력 후 전송 시 컨트롤러에 /board/addReply.do로 요청한다. --%>
<form name="frmReply" method="post" action="${contextPath}/board/addReply.do" enctype="multipart/form-data">
<table align="center">
<tr>
<td align="right"> 글쓴이: </td>
<td><input type="text" size="5" value="lee" disabled /> </td>
</tr>
<tr>
<td align="right">글제목: </td>
<td><input type="text" size="67" maxlength="100" name="title" /></td>
</tr>
<tr>
<td align="right" valign="top"><br>글내용: </td>
<td><textarea name="content" rows="10" cols="65" maxlength="4000"> </textarea> </td>
</tr>
<tr>
<td align="right">이미지파일 첨부: </td>
<td> <input type="file" name="imageFileName" onchange="readURL(this);" /></td>
<td><img id="preview" src="#" width=200 height=200 /></td>
</tr>
<tr>
<td align="right"> </td>
<td>
<input type=submit value="답글반영하기" />
<input type=button value="취소" onClick="backToList(this.form)" />
</td>
</tr>
</table>
</form>
</body>
</html>
6. 다음 주소로 요청하여 게시글 중 부모 글을 클릭한다.
- http://localhost:8090/pro17/board/listArticles.do
7. 글 상세창에서 답글쓰기를 클릭한다.
8. "수정된 상품 후기입니다..."에 대한 답글("이용 후기입니다.")을 작성한 후 답글반영하기를 클릭한다.
9. 내가 작성한 답글의 내용을 한 번 더 표시한다.
10. 글 목록을 보면 답글이 추가된 것을 볼 수 있다.
11. 또한 답글에 추가한 이미지도 폴더에 저장된다.
'Java > Java_JSP Model2' 카테고리의 다른 글
(11) 모델2 방식으로 효율적으로 개발하기 4_답변형 게시판 구현 8 (0) | 2023.09.26 |
---|---|
(9) 모델2 방식으로 효율적으로 개발하기 4_답변형 게시판 구현 6 (0) | 2023.09.26 |
(8) 모델2 방식으로 효율적으로 개발하기 4_답변형 게시판 구현 5 (0) | 2023.09.26 |
(7) 모델2 방식으로 효율적으로 개발하기 4_답변형 게시판 구현 4 (0) | 2023.09.26 |
(6) 모델2 방식으로 효율적으로 개발하기 4_답변형 게시판 구현 3 (0) | 2023.09.23 |