[Web] 답글 쓰기 구현하기 (게시판 구현)
2017. 4. 13. 14:59ㆍJava/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 |