- 답글
- viewArticle.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<c:set var="contextPath" value="${pageContext.request.contextPath}"/>
<%
request.setCharacterEncoding("utf-8");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>글 상세 보기</title>
<style type="text/css">
#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("title").disabled=false;
document.getElementById("content").disabled=false;
let imageFileName=document.getElementById("imageFileName");
if (imageFileName!=null&&imageFileName!=''){
imageFileName.disabled=false;
}
document.getElementById("btn_modify").style.display="block";
document.getElementById("btn_all").style.display="none";
}
//이미지파일 첨부시 미리보기 기능 구현
function readURL(input){
if (input.files && input.files[0]){
let reader=new FileReader();
reader.onload=function(event){
$('#preview').attr('src',event.target.result);
}
reader.readAsDataURL(input.files[0]);
}
}
//수정 반영 버튼
function fn_modify(obj){
obj.action="${contextPath}/board/modArticle.do"
obj.submit();
}
//삭제
function fn_remove(url, articleNo){
let newForm = document.createElement("form");
newForm.setAttribute("method","post");
newForm.setAttribute("action",url);
let articleNoInput=document.createElement("input");
articleNoInput.setAttribute("type","hidden");
articleNoInput.setAttribute("name","articleNo");
articleNoInput.setAttribute("value",articleNo);
newForm.appendChild(articleNoInput);
document.body.appendChild(newForm);
newForm.submit();
}
////replyForm.do 컨트롤러로 이동
function fn_reply(url, parentNo){ //전달받은 글번호를 부모글번호로 사용
let reForm=document.createElement("form");
reForm.setAttribute("method","post");
reForm.setAttribute("action",url);
let parentNoInput=document.createElement("input");
parentNoInput.setAttribute("type","hidden");
parentNoInput.setAttribute("name","parentNo");
parentNoInput.setAttribute("value",parentNo);
reForm.appendChild(parentNoInput);
document.body.appendChild(reForm);
reForm.submit(); //replyForm.do로 이동 (BoardController)
}
</script>
<body>
<form name="formArticle" method="post" enctype="multipart/form-data">
<table align="center">
<tr>
<td width="150" align="center" bgcolor="beige">글번호</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="beige">아이디</td>
<td><input type="text" name="id" id="id"
value="${article.id}" disabled></td>
</tr>
<tr>
<td width="150" align="center" bgcolor="beige">제목</td>
<td><input type="text" name="title" id="title" value="${article.title}" disabled></td>
</tr>
<tr>
<td width="150" align="center" bgcolor="beige">내용</td>
<td><textarea name="content" id="content" disabled>${article.content}</textarea></td>
</tr>
<c:if test="${not empty article.imageFileName && article.imageFileName !='null'}">
<tr>
<td width="150" align="center" bgcolor="beige">이미지</td>
<td>
<input type="hidden" name="original" value="${article.imageFileName}">
<img src="${contextPath}/download.do?articleNo=${article.articleNo}&
imageFileName=${article.imageFileName}" id=preview><br>
</td>
</tr>
<tr>
<td>
<input colspan="2" type="file" name="imageFileName" id="imageFileName" disabled onchange="readURL(this);">
</td>
</tr>
</c:if>
<tr>
<td width="150" align="center" bgcolor="beige">등록일자</td>
<td>
<input type="text" value="<fmt:formatDate value="${article.writeDate}"/>" disabled>
</td>
</tr>
<tr id="btn_modify">
<td colspan="2" align="center">
<input type="button" value="수정 반영" onclick="fn_modify(formArticle)">
<input type="button" value="리스트로 돌아가기" onclick="backToList(formArticle)">
</td>
</tr>
<tr>
<td id="btn_all">
<input colspan="2" type="button" value="수정하기" onclick="fn_enable(this.form);">
<input colspan="2" type="button" value="삭제하기" onclick="fn_remove('${contextPath}/board/removeArticle.do', ${article.articleNo} );">
<input colspan="2" type="button" value="돌아가기" onclick="backToList(formArticle);">
<input colspan="2" type="button" value="답글쓰기" onclick="fn_reply('${contextPath}/board/replyForm.do', ${article.articleNo} );"> ////
</td>
</tr>
</table>
</form>
</body>
</html>
// form 속성으로 method 대신 action="post"으로 오기입해, articleNo가 전달 안 되는 오류 발생했었음 실수 유의
- BoardController
@WebServlet("/board/*")
public class BoardController extends HttpServlet {
private static String ART_IMAGE_REPO="C:\\server\\upload_images";
HttpSession session; ////
BoardService bs;
ArticleVO vo;
public void init(ServletConfig config) throws ServletException {
bs= new BoardService();
vo= 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");
String action=request.getPathInfo();
System.out.println("요청명 : "+action);
List<ArticleVO> articleList = new ArrayList<ArticleVO>();
~
} else if (action.equals("/replyForm.do")) { ////
int parentNo = Integer.parseInt(request.getParameter("parentNo"));
//viewArticle 뷰에서 답글 버튼 클릭 시 fn_reply 함수 실행
//-> 함수 내에서 생성한 폼 submit
//-> input에 setAttribute()으로 붙인 파라미터(name, value) 전달받음
session = request.getSession(); ////replyForm.jsp 거쳐 BoardController 내 addReply.do로 전달 위해
session.setAttribute("parentNo", parentNo); //부모글번호 답글 폼으로 전달
nextPage="/boardView/replyForm.jsp"; ////답글 폼으로 이동
}
RequestDispatcher rd = request.getRequestDispatcher(nextPage);
rd.forward(request, response);
}
//자료,이미지 업로드 처리 메소드
private Map<String, String> upload(HttpServletRequest request, HttpServletResponse response) {
Map<String, String> articleMap = new HashMap<String, String>();
String encoding = "utf-8";
File currentDirPath = new File(ART_IMAGE_REPO); //
DiskFileItemFactory factory = new DiskFileItemFactory(); //파일 업로드, 저장할 저장소
factory.setRepository(currentDirPath); //저장소 위치 설정
factory.setSizeThreshold(1024*1024); //저장할 최대 크기
ServletFileUpload upload = new ServletFileUpload(factory);
try {
List items = upload.parseRequest(request); //요청 객체로부터 받은 HTTP Body 변환, List 반환
for (int i=0;i<items.size();i++) {
FileItem fileItem = (FileItem) items.get(i);
//fileitem : 업로드한 파일 or input에 입력된 텍스트
if (fileItem.isFormField()) { //파일 형식 X (텍스트 등)
System.out.println(fileItem.getFieldName()+" : "+fileItem.getString(encoding));
articleMap.put(fileItem.getFieldName(), fileItem.getString(encoding));
} else { //파일 형식 O
System.out.println("파라미터 이름 : "+fileItem.getFieldName());
System.out.println("파일 이름 : "+fileItem.getName());
System.out.println("파일 크기 : "+fileItem.getSize()+"바이트");
if (fileItem.getSize()>0) {
//String separator=File.separator; //OS별 구분자
//int index = fileItem.getName().lastIndexOf(separator); //마지막에 위치한 구분자 인덱스
String fileName = fileItem.getName();
//.substring(index+1); //구분자 뒤 텍스트 가져옴 (파일명)
System.out.println("filename : "+fileName);
articleMap.put(fileItem.getFieldName(), fileName); //getFieldName : 요청파라미터명
File uploadFile = new File(currentDirPath + "/temp/" + fileName); //파일 객체 생성 ////
fileItem.write(uploadFile); //실제로 저장소에 파일 생성, 저장
}
}
}
} catch (Exception e) {
System.out.println("업로드 중 에러");
e.printStackTrace();
}
return articleMap;
}
}
- replyForm.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<c:set var="contextPath" value="${pageContext.request.contextPath}"/>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>답글쓰기</title>
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script type="text/javascript">
//글쓰기 완료 => 목록으로 돌아가기
function listView(obj){
obj.action="${contextPath}/board/listArticles.do";
obj.submit();
}
//이미지파일 첨부시 미리보기 기능
function readURL(input){
if (input.files && input.files[0]){
let reader=new FileReader();
reader.onload=function(event){
$('#preview').attr('src',event.target.result);
}
reader.readAsDataURL(input.files[0]);
}
}
</script>
</head>
<body>
<h2 align="center">답글쓰기</h2>
<form name="formReply" action="${contextPath}/board/addReply.do" ////BoardController 이동
method="post" enctype="multipart/form-data">
<table align="center">
<tr>
<td align="right">글쓴이 </td>
<td><input type="text" value="chanho" disabled></td>
</tr>
<tr>
<td align="right">글제목 </td>
<td><input type="text" name="title"></td>
</tr>
<tr>
<td align="right">글내용 </td>
<td><textarea rows="10" cols="50" maxlength="4000" name="content"></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="center" colspan="2">
<input type="submit" value="답글 반영">
<input type="button" value="취소" onclick="listView(this.form);">
</td>
</tr>
</table>
</form>
</body>
</html>
- BoardController
} else if (action.equals("/addReply.do")) { //답변 submit
session=request.getSession(); ////
int parentNo = (Integer) session.getAttribute("parentNo");
//BoardController -> replyForm.do 거쳐 온 세션값
session.removeAttribute("parentNo");
Map<String, String> articleMap = upload(request,response);
String title = articleMap.get("title");
String content = articleMap.get("content");
String imageFileName = articleMap.get("imageFileName");
vo.setParentNo(parentNo);
vo.setId("chanho");
vo.setTitle(title);
vo.setContent(content);
vo.setImageFileName(imageFileName);
int articleNo = bs.addReply(vo);
////BoardService로 부모글번호 & 인풋값 세팅한 vo 객체 전달 => 글 번호 받아옴
if (imageFileName != null && imageFileName.length() != 0) {
File srcFile = new File(ART_IMAGE_REPO+"/temp/"+imageFileName);
File destFile = new File(ART_IMAGE_REPO+"/"+articleNo);
destFile.mkdir();
FileUtils.moveFileToDirectory(srcFile, destFile, true);
}
PrintWriter pw = response.getWriter();
pw.print("<script>"
+ "alert('답글을 추가했습니다');"
+ "location.href='"+request.getContextPath()+"/board/viewArticle.do?"
+ "articleNo=" + articleNo + "';"
+ "</script>");
return;
}
- BoardService
public int addReply(ArticleVO vo) {
return dao.insertNewArticle(vo);
}
- BoardDAO
//새글 번호 생성 메소드
private int getNewArticleNo() {
try {
con=dataFactory.getConnection();
String query = "select max(articleNo) from board_qna"; //현재 글번호 중 가장 큰 숫자
System.out.println(query);
ps = con.prepareStatement(query);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
return rs.getInt(1)+1;
}
}catch(Exception e) {
System.out.println("글 번호 생성 중 에러");
}
return 1; //글번호 없으면 1 부여
}
//글쓰기 저장 메소드
public int insertNewArticle(ArticleVO vo) { //void->int
int articleNo = getNewArticleNo(); //
try {
con=dataFactory.getConnection();
int parentNo = vo.getParentNo();
String title = vo.getTitle();
String content = vo.getContent();
String id = vo.getId();
String imageFileName = vo.getImageFileName();
String query = "insert into board_qna(articleno, parentno, title, content, id, imagefile)"
+ "values(?,?,?,?,?,?)";
System.out.println(query);
ps=con.prepareStatement(query);
ps.setInt(1, articleNo);
ps.setInt(2, parentNo);
ps.setString(3, title);
ps.setString(4, content);
ps.setString(5, id);
ps.setString(6, imageFileName);
ps.executeUpdate();
ps.close();
con.close();
} catch (Exception e) {
System.out.println("DB 글쓰기 저장 중 에러");
}
return articleNo; //
}
- 페이징
- BoardController
if (action==null||action.equals("/listArticles.do")) {
////페이징 기능
String _section = request.getParameter("section");
String _pageNum = request.getParameter("pageNum");
//첫 방문시 section, pageNum 파라미터 null -> 1로 값세팅
int section = Integer.parseInt((_section)==null?"1":_section);
int pageNum = Integer.parseInt((_pageNum)==null?"1":_pageNum);
Map<String, Integer> pagingMap = new HashMap<String, Integer>();
pagingMap.put("section", section);
pagingMap.put("pageNum", pageNum);
Map articleMap = bs.listArticles(pagingMap); ////페이징 처리한 글목록, 전체 게시글 수 리턴
articleMap.put("section", section);
articleMap.put("pageNum", pageNum);
request.setAttribute("articleMap", articleMap);
articleList = bs.listArticles(); ////전체 글 목록
request.setAttribute("articleList", articleList);
nextPage="/boardView/listArticles.jsp";
}
- BoardService
////섹션, 페이지 값 통해 페이징 처리한 글목록, 전체 게시글 수 리턴
public Map listArticles(Map<String, Integer> pagingMap) { //섹션, 페이지 값 파라미터로 전달됨
Map articleMap = new HashMap();
List<ArticleVO> articleList = dao.selectAllArticles(pagingMap); ////페이징 처리한 글 목록
int totArticles = dao.selectTotArticles(); ////전체 게시글 수
articleMap.put("articleList", articleList);
articleMap.put("totArticles", totArticles);
return articleMap;
}
//전체 글 목록
public List<ArticleVO> listArticles(){
List<ArticleVO> articleList = dao.selectAllArticles();
return articleList;
}
- BoardDAO
////페이징 - 섹션당 100개, 페이지당 10개 게시글 표시 (메소드 오버로딩)
public List selectAllArticles(Map pagingMap){
List articleList = new ArrayList<ArticleVO>();
//페이징 기능 추가
int section = (Integer) pagingMap.get("section"); //섹션 값
int pageNum = (Integer) pagingMap.get("pageNum"); //페이지 값
try {
con=dataFactory.getConnection();
/*
* String query = "select * from (select rownum as recnum from board_qna" +
* " order by articleno desc)" +
* " where recnum between (?-1)*100 + (?-1)*10+1 and (?-1)*100 + ?*10";
* ps=con.prepareStatement(query); ps.setInt(1, section); //11페이지부터 ps.setInt(2,
* pageNum); //페이지 수 ps.setInt(3, section); ps.setInt(4, pageNum);
*/
// 1. 기존 계층형 구조로 글 목록 조회
// 2. 그 결과에 대해 다시 ROWNUM(recNum)이 표시되도록 서브 쿼리문으로 다시 조회
// 3. ROWNUM이 표시된 두 번째 결과에서 where 조건에 맞는 ROWNUM이 있는 레코드들만 최종적 조회
String query = "SELECT * FROM (SELECT rownum as recnum, lvl, articleno,"
+ " parentno, title, id, writedate FROM (SELECT level as lvl, articleno,"
+ " parentNo, title, id, writedate FROM board_qna START WITH parentno=0"
+ " CONNECT BY PRIOR articleno=parentno ORDER SIBLINGS BY articleno DESC))"
+ " where recnum between (?-1)*100 + (?-1)*10+1 and (?-1)*100 + ?*10";
System.out.println(query);
ps=con.prepareStatement(query);
ps.setInt(1, section);
ps.setInt(2, pageNum);
ps.setInt(3, section);
ps.setInt(4, pageNum);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
int level = rs.getInt("lvl");
int articleNo = rs.getInt("articleno");
int parentNo = rs.getInt("parentno");
String title = rs.getString("title");
String id = rs.getString("id");
Date writeDate = rs.getDate("writedate");
ArticleVO vo = new ArticleVO();
vo.setArticleNo(articleNo);
vo.setLevel(level);
vo.setParentNo(parentNo);
vo.setTitle(title);
vo.setId(id);
vo.setWriteDate(writeDate);
articleList.add(vo);
}
} catch (Exception e) {
System.out.println("DB 페이징 처리 중 에러");
}
return articleList;
}
//전체 게시글 수
public int selectTotArticles() {
try {
con=dataFactory.getConnection();
String query = "select count(articleno) from board_qna";
System.out.println(query);
ps=con.prepareStatement(query);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
return rs.getInt(1);
}
rs.close();
ps.close();
con.close();
} catch(Exception e) {
System.out.println("DB 전체 게시글 수 처리 중 에러");
}
return 0;
}
//게시판 글 목록
public List<ArticleVO> selectAllArticles(){
List<ArticleVO> articleList = new ArrayList<ArticleVO>();
try {
con=dataFactory.getConnection();
//오라클 - 계층형 쿼리
String query="SELECT LEVEL, articleNo, parentNo, title, content, id, writeDate from board_qna"
+ " START WITH parentNo=0 CONNECT BY PRIOR articleNo=parentNo"
+ " ORDER SIBLINGS BY articleNo DESC";
System.out.println(query);
ps=con.prepareStatement(query);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
int level = rs.getInt(1);
int articleNo = rs.getInt(2);
int parentNo = rs.getInt(3);
String title = rs.getString(4);
String content = rs.getString(5);
String id = rs.getString(6);
Date writeDate = rs.getDate(7);
ArticleVO article = new ArticleVO();
article.setLevel(level);
article.setArticleNo(articleNo);
article.setParentNo(parentNo);
article.setTitle(title);
article.setContent(content);
article.setId(id);
article.setWriteDate(writeDate);
articleList.add(article);
}
rs.close();
ps.close();
con.close();
} catch(Exception e) {
System.out.println("DB 글목록 조회 중 에러");
}
return articleList;
}
- listArticles.jsp 수정
<c:set var="articleList" value="${articleMap.articleList}"/> ////페이징 처리한 글 목록
<c:set var="totArticles" value="${articleMap.totArticles}"/> ////전체 게시글 수
<c:set var="section" value="${articleMap.section}"/>
<c:set var="pageNum" value="${articleMap.pageNum}"/>
~
<style>
.sel_page{
color:red;
}
a {
text-decoration:none;
color:black;
}
</style>
~
<div>
<c:if test="${totArticles != null }" >
<c:choose>
<c:when test="${totArticles>100}"> <!-- 글 개수 100개 초과 -->
<c:forEach var="page" begin="1" end="10" step="1">
<c:if test="${section>1&&page==1}">
<a href="${contextPath}/board/listArticles.do?section=${section-1}
&pageNum=${(section-1)*10+1}">prev</a> <!-- 이전 섹션 -->
</c:if>
<a href="${contextPath}/board/listArticles.do?section=${section}&pageNum=${page}">
${(section-1)*10+page}</a>
<c:if test="${page==10}">
<a href="${contextPath}/board/listArticles.do?section=${section+1}
&pageNum=${section*10+1}">next</a> <!-- 다음 섹션 -->
</c:if>
</c:forEach>
</c:when>
<c:when test="${totArticles==100}"> <!-- 글 개수 100개 -->
<c:forEach var="page" begin="1" end="10" step="1">
<a href="#">${page}</a>
</c:forEach>
</c:when>
<c:when test="${totArticles<100}"><!-- 글 개수 100개 미만 -->
<c:forEach var="page" begin="1" end="${totArticles/10+1}" step="1" >
<c:choose>
<c:when test="${page==pageNum}">
<a class="sel_page" href="${contextPath}/board/listArticles.do?
section=${section}&pageNum=${page}">${page}</a>
</c:when>
<c:otherwise>
<a class="no_page" href="${contextPath}/board/listArticles.do?
section=${section}&pageNum=${page}">${page}</a>
</c:otherwise>
</c:choose>
</c:forEach>
</c:when>
</c:choose>
</c:if>
</div>
※ MemberVO 오류 수정
이미지 없는 글 조회시 imageFileName 에 예전 값이 그대로 들어가 숨김처리한 태그가 보여지는 오류 발생
public void setImageFileName(String imageFileName) {
try {
if (imageFileName != null && imageFileName.length()!=0) {
this.imageFileName=URLEncoder.encode(imageFileName,"utf-8");
} else { ////
this.imageFileName=null; ////
}
} catch(UnsupportedEncodingException e) {
System.out.println("이미지 파일 이름 저장 중 에러");
}
}
setter에 else문을 누락해 발생했었음
'Programming > 국비학원' 카테고리의 다른 글
221103 - MVC - 글 수정, 삭제 (0) | 2022.11.04 |
---|---|
221101 - MVC - 글 상세 조회 기능 (0) | 2022.11.03 |
221031 - MVC - 글 작성 기능 (0) | 2022.11.01 |
221028 - MVC 게시판 - 문의게시판 목록(계층형 쿼리), 글 작성 (0) | 2022.10.29 |
221027 - MVC - 회원정보 수정, 삭제 / 문의게시판 (미완) (0) | 2022.10.28 |