Notice
Recent Posts
Recent Comments
Link
- Outsider's Dev Story
- 흔한 개발자의 개발 노트
- 내 아이큐 80, 너도 80, 둘이 합쳐 160 - 내…
- 조대협의 블로그
- 자바캔(Java Can Do IT) / 최범균 님
- Sangon, Han – 개발자; 읽고 생각하고 쓰고 …
- The Evolution of the Web
- NAVER D2
- Dashboard - SLiPP
- ITWorld Korea - 테크놀로지 리더를 위한 글…
- OKKY - All That Developer
- 웹Frameworks
- 오픈튜토리얼스
- 위키독스 / 온라인 책 제작 공유 플랫폼 서비스
- 블로터
- IT OnAir
- 한 처음에 / 백창92
- Divide my knowledge / 완프최
- SERI.org
Bigfat
[Web] 답글 쓰기 구현하기 (게시판 구현) 본문
게시글에 답글을 쓰는 기능을 구현해보자
이전과 동일하게 bbs.properties파일에 요청에 대해 처리할 클래스를 매핑한다.
#bbs.properties /writeForm.bbs=com.edu.bbs.WriteFormImpl /write.bbs=com.edu.bbs.WriteImpl /list.bbs=com.edu.bbs.ListImpl /content.bbs=com.edu.bbs.ContentImpl /login.bbs=com.edu.bbs.LoginImpl /logout.bbs=com.edu.bbs.LogoutImpl /updateForm.bbs=com.edu.bbs.UpdateFormImpl /update.bbs=com.edu.bbs.UpdateImpl /delete.bbs=com.edu.bbs.DeleteImpl /replyForm.bbs=com.edu.bbs.ReplyFormImpl /reply.bbs=com.edu.bbs.ReplyImpl
content.jsp에서 가지고 있던 해당 게시글에 대한 pageNum, depth, groupId, pos 값을 받아 다시 HttpServletRequest 객체에 심는다.
package com.edu.bbs; import java.io.UnsupportedEncodingException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ReplyFormImpl implements BBSService { @Override public String bbsService(HttpServletRequest req, HttpServletResponse resp) throws ServletException, UnsupportedEncodingException { req.setAttribute("pageNum", req.getParameter("pageNum")); req.setAttribute("depth", req.getParameter("depth")); req.setAttribute("groupId", req.getParameter("groupId")); req.setAttribute("pos", req.getParameter("pos")); return "/replyForm.jsp"; } }
replyForm.jsp를 생성하고 아래와 같이 작성한다. '취소'버튼은 로그인, 로그아웃을 구현하면서 공부한 브라우저 히스토리 객체를 사용하였다. 뭐든 모르는 것보단 역시 알고 있는게 좋다.
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>BBS Reply</title> <link href="bootstrap-3.3.7/css/bootstrap.min.css" rel="stylesheet"> <style> #contentForm { width: 40%; margin: 0 auto; padding-top: 12%; } .table > thead > tr > th, .table > tbody > tr > th { background-color: #e6ecff; text-align: center; } </style> </head> <body> <form action="/bbs/reply.bbs" method="post"> <div id="contentForm"> <input type="hidden" name="pageNum" value="${pageNum}"> <input type="hidden" name="groupId" value="${groupId}"> <input type="hidden" name="depth" value="${depth}"> <input type="hidden" name="pos" value="${pos}"> <div class="input-group input-group-sm" role="group" aria-label="..."> <table class="table table-striped table-bordered"> <thead> <tr> <th width="30%">글쓴이</th> <td width="70%">${id}</td> </tr> <tr> <th style="padding-bottom: 15px;">제목</th> <td><input type="text" name="title" value="[Re] " class="form-control" aria-describedby="basic-addon1"></td> </tr> </thead> <tbody> <tr> <td colspan="2"> <textarea class="form-control" rows="10" name="content" placeholder="글을 적어 주세요."></textarea> </td> </tr> <tr> <th style="padding-top: 15px">첨부파일</th> <td><input type="file" class="btn btn-default" name="fileName"></td> </tr> </tbody> </table> </div> <div class="btn-group btn-group-sm" role="group" aria-label="..."> <input type="submit" class="btn btn-default" value="글쓰기"> <input type="reset" class="btn btn-default" value="초기화"> <input type="button" class="btn btn-default" value="취소" onclick="window.history.back()"> </div> </div> </form> </body> </html>
/bbs/reply.bbs 요청을 처리하는 ReplyImpl클래스를 작성한다.
package com.edu.bbs; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ReplyImpl implements BBSService { @Override public String bbsService(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf-8"); BBSDto article = new BBSDto(); article.setId(req.getSession().getAttribute("id").toString()); article.setTitle(req.getParameter("title")); article.setContent(req.getParameter("content")); article.setGroupId(Integer.parseInt(req.getParameter("groupId"))); article.setDepth(Integer.parseInt(req.getParameter("depth"))); article.setPos(Integer.parseInt(req.getParameter("pos"))); article.setFileName(req.getParameter("fileName")); try { BBSOracleDao.getInstance().replyArticle(article); } catch (Exception e) { e.printStackTrace(); } // 이중 submit을 막기 위해 redirect 해준다. resp.sendRedirect("/bbs/list.bbs?pageNum="+req.getParameter("pageNum")); return null; } }
BBSOracleDao클래스에 아래 두 메서드를 작성한다. group_id는 부모글의 article_number이며, 이 부모글의 답글(자식글)은 같은 group_id를 가진다. depth는 답글의 깊이이며 부모글은 0, 이 부모글의 답글은 1, 이 답글의 답글은 depth가 2가 된다. pos는 이 답글들의 순서를 조절하게 된다. 같은 depth의 답글은 존재할 수 있지만 pos값으로 순서를 조절할 수 있다는 것이다. group_id, depth 그리고 pos는 직접 연습장에 그려가면서 공부해보는 것이 좋다. 예전에 지금과 같이 무한 댓글 기능을 구현하면서 오라클의 계층형 구조 쿼리를 사용하였었는데 스프링프레임워크로 다시 게시판을 구현할 때를 위해 현재의 방법을 택했다.
/** * BBSOracleDao클래스에 추가한다. * @param groupId * @param pos * @return */ public synchronized int upPos(int groupId, int pos) throws ClassNotFoundException, SQLException { conn = orclDbc.getConnection(); query = new StringBuffer(); query.append("UPDATE bbs"); query.append(" SET pos = pos + 1"); query.append(" WHERE group_id = ?"); query.append(" AND pos > ?"); pstmt = conn.prepareStatement(query.toString()); pstmt.setInt(1, groupId); pstmt.setInt(2, pos); int result = pstmt.executeUpdate(); disconnect(); return result; } public synchronized int replyArticle(BBSDto article) throws ClassNotFoundException, SQLException { BBSOracleDao.getInstance().upPos(article.getGroupId(), article.getPos()); conn = orclDbc.getConnection(); query = new StringBuffer(); query.append("INSERT INTO bbs "); query.append("VALUES(bbs_seq.nextval, ?, ?, ?, ?, ?, ?, 0, sysdate, ?)"); pstmt = conn.prepareStatement(query.toString()); pstmt.setString(1, article.getId()); pstmt.setString(2, article.getTitle()); pstmt.setString(3, article.getContent()); // groupId는 그대로 삽입한다. pstmt.setInt(4, article.getGroupId()); // depth는 1 증가시킨다. pstmt.setInt(5, article.getDepth() + 1); // pos은 부모글 아래에 이미 답글이 있으면 모두 1씩 증가시킨 후(upPos메서드가 수행), // 현재 삽입하는 게시글의 pos를 1 증가시킨다. pstmt.setInt(6, article.getPos() + 1); pstmt.setString(7, article.getFileName()); int result = pstmt.executeUpdate(); disconnect(); return result; }
코드를 확인하면서 list.jsp에서 depth에 따라 공백을 주는 코드가 잘못된 것을 발견하고 수정하였다. <tbody>태그 내용을 아래와 같이 수정하자.
<tbody> <c:forEach var="article" items="${articles}" varStatus="status"> <tr> <td>${article.articleNumber}</td> <td id="title"> <c:if test="${article.depth > 0}"> <c:forEach var="i" begin="1" end="${article.depth}"> <span> </span> </c:forEach> </c:if> <a href="/bbs/content.bbs?articleNumber=${article.articleNumber}&pageNum=${pageNum}" id="writeLink">${article.title}</a> <c:if test="${article.hit >= 20}"> <span class="hit">hit!</span> </c:if> </td> <td>${article.id}</td> <td>${article.writeDate}</td> <td>${article.hit}</td> <tr> </c:forEach> </tbody>
아래 답글 쓰기 기능 구현에 대한 시연 화면이다. 사실은 답글 쓰기를 제대로 구현하려면 삭제 기능에 추가적인 작업을 해줘야한다. 네이버처럼 부모글을 지우면 그 부모글의 자식글을 남기는 대신 부모글이 삭제되었다는 정보를 남겨줘야 하는데, 나중에 시간이 되면 추가할 계획이 있다.
'Java > web' 카테고리의 다른 글
[Spring] 스프링 프로젝트 생성 및 웹 애플리케이션 실행하기 (0) | 2017.04.17 |
---|---|
[Web] Ajax를 이용한 댓글 쓰기 구현하기 (게시판 구현) (10) | 2017.04.13 |
[Web] 글 삭제 구현하기 (게시판 구현) (0) | 2017.04.13 |
[Web] 부트스트랩 적용하기, Pagination (게시판 구현) (0) | 2017.04.12 |
[Web] 글 수정 화면 만들기 (게시판 구현) (2) | 2017.04.11 |