답변형(계층형) 게시판을 구현하기 전에 로직을 먼저 이해해야한다.
실제 DB에는 아래와 같이 출력이 되는 상태
글 목록을 뿌리면 ORDER BY seq DESC로 시퀀스에 따라 마지막에 작성한 글이 맨 위로 올라오게 되는데 이렇게 되면 답글을 달았던 글이 답글을 단 글 밑에 보여지는게 아니라 맨위로 출력이 되게 된다.
계층형은 이렇게 보이면 안되고 게시글에 답글을 달면 해당 게시글 밑에 들여쓰기가 되어진 상태로 출력이 되어져야 한다.
계층형으로 답글을 출력하기 위해서는 해결 방법이 2가지가 있다.
첫 번째 해결 방법은 하나의 게시글을 묶어줄 수 있는 그룹화 컬럼(REF)을 추가하고 그룹 안에서도 순서를 지정할 수 있는 컬럼(STEP)을 추가한다. 그리고 새글인지 답글인지 답답글인지 파악하여 들여쓰기를 할 수 있도록 DEPTH 컬럼을 추가한다.
두 번째 해결 방법은 하나의 게시글을 묶어줄 수 있는 그룹화 컬럼(REF)과 그룹 안에서 순서를 주는 컬럼(STEP)을 하나로 묶어서 STEP이라는 컬럼을 추가하고 새글인지 답글인지 답답글인지 파악하여 들여쓰기를 할 수 있도록 DEPTH 컬럼을 추가한다.
오늘 수업에서는 첫 번째 해결 방법으로 답변형(계층형) 게시판을 구현하였다.
첫 번째 해결 방법으로 구현하기 위해서는 아래 규칙을 지켜주어야 한다.
1) 첫 번째 게시글(새글) 쓰기
ㄱ. 글 그룹(REF)은 게시글 번호(seq)랑 동일한 값으로 설정
ㄴ. 새글의 순번(STEP)은 0 또는 1로 설정한다.
ㄷ. 새글의 깊이(DEPTH)는 0 또는 1로 설정한다.
2) 답글 쓰기
*** Point! 어떤 부모 글에 답글을 다는지 알아야함
ㄱ. 글 번호(seq)는 부모 글의 그룹(REF)과 동일한 값으로 설정한다.
ㄴ. (1) 동일한 글그룹(REF)에서 부모 순번(STEP)보다 큰 순번(STEP)들의 값을 1 증가 시킴
(2) 그 후 새 글의 순번(STEP)을 부모 순번(STEP) +1 값으로 설정한다.
ㄷ. (1)동일한 글그룹(REF)에서 부모 깊이(DEPTH)
(2) 새글의 깊이(DEPTH)는 부모 깊이(DEPTH) +1 값으로 설정한다.
구현 과정
테이블 + 시퀀스 생성을 한 후.. (n) 순번에 따라서 구현하였다.
days10.replyboard.controller - MVC의 C 컨트롤러
ㄴ DispatcherServlet.java (4) days09 복붙 후 수정
ㄴ web.xml 서블릿 컨트롤러 등록 (4)
ㄴ days10/replyboard/commandHandler.properties (6) days09 복붙 후 수정
days10.replyboard.command - MVC의 M 모델
ㄴ CommandHandler.java (5) days09 복붙 후 수정
ㄴ ListHandler.java (7)
ㄴ WriteHandler.java (8)
days10.replyboard.domain - DTO
ㄴ ReplyBoardDTO.java (1)
days10.replyboard.persistence - DAO
ㄴ IReplyBoard.java 인터페이스 (2)
ㄴ ReplyBoardDAO.java (3)
days10.replyboard.service - SERVICE (9)
ㄴ ListService.java
ㄴ ViewService.java
ㄴ WriteService.java
ReplyBoardDTO
package days10.replyboard.domain;
import java.util.Date;
public class ReplyBoardDTO {
// fields
private int num; // 글번호
private String writer; // 작성자
private String email; // 이메일
private String subject; // 제목
private String pass; // 비밀번호
private int readcount; // 조회수
private Date regdate; // 등록일
private String content; // 내용
private String ip; // IP주소
private int ref; // *** 그룹
private int step; // *** 그룹내 순번
private int depth; // *** 깊이
// 새로운 게시글 일경우 new 이미지 붙이기 위한 필드 추가
private boolean newImg; // new 마크
// getter, setter
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getWriter() {
return writer;
}
public void setWriter(String writer) {
this.writer = writer;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getPass() {
return pass;
}
public void setPass(String pass) {
this.pass = pass;
}
public int getReadcount() {
return readcount;
}
public void setReadcount(int readcount) {
this.readcount = readcount;
}
public Date getRegdate() {
return regdate;
}
public void setRegdate(Date regdate) {
this.regdate = regdate;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public int getRef() {
return ref;
}
public void setRef(int ref) {
this.ref = ref;
}
public int getStep() {
return step;
}
public void setStep(int step) {
this.step = step;
}
public int getDepth() {
return depth;
}
public void setDepth(int depth) {
this.depth = depth;
}
public boolean isNewImg() {
return newImg;
}
public void setNewImg(boolean newImg) {
this.newImg = newImg;
}
}
IReplyBoard 인터페이스
ReplyBoardDAO
DispatcherServlet와 commandHandler.properties
CommandHandler 인터페이스와 인터페이스를 구현한 클래스들
CommandHandler 인터페이스
NullHandler
ListHandler
WriteHandler
ViewHandler
Service
ListService
WriteService
ViewServie
list.jsp
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>2022. 6. 27. - 오전 11:14:14</title>
<link rel="shortcut icon" type="image/x-icon" href="../images/SiSt.ico">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<style>
a {
text-decoration: none;
color: black;
}
table, tr, td {
border:solid 1px gray;
border-radius: 3px;
padding: 5px;
font-size: 12px;
}
/*
tr.data:nth-last-child(odd) {
background: gray;
}
tr.data:nth-last-child(even) {
background: #EFEFEF;
}
*/
tr.data:hover {
background: #EFEFEF;
}
select , input{
vertical-align:middle;
}
</style>
<script>
$(function (){
$('#searchBtn').click(function (){
// jquery 사용하는 선택자 :first
// attr() 속성을 설정하거나 가져오는 함수
$('form:first').attr('action','list.do');
$('form:first').attr('method','get');
$('form:first').submit();
});
});
</script>
</head>
<body>
<h3>days10 - list.jsp</h3>
<h3 style="text-align: center">계층형 게시판</h3>
<table style="width:700px;margin:50px auto" border="1" >
<tr>
<td align="right" colspan="6">
<a href="write.do">글쓰기</a>
</td>
</tr>
<tr style="background:gray;color:white;font-weight:bold">
<td width="50" align="center">번호</td>
<td width="280" align="center">제 목</td>
<td width="100" align="center">작성자</td>
<td width="120" align="center">작성일</td>
<td width="50" align="center">조회</td>
<td width="100" align="center">IP</td>
</tr>
<tbody>
<!-- request.setAtttribute("list", ??); -->
<c:if test="${ empty list }">
<tr class="data">
<td align="center" colspan="6">
<h3>작성된 게시글이 없습니다.</h3>
</td>
</tr>
</c:if>
<c:if test="${ not empty list }">
<c:forEach items="${ list }" var="dto">
<tr class="data">
<td align="center">${ dto.num }</td>
<td>
<c:if test="${ dto.depth gt 0 }">
<img width="${ dto.depth*15 }px" >
<img src="/jspPro/days10/replyboard/images/arr.gif" alt="" />
</c:if>
<a href="view.do?num=${ dto.num }&page=${ param.page }&searchCondition=${ param.searchCondition }&searchWord=${ param.searchWord }">${ dto.subject }</a>
<c:if test="${ dto.newImg }">
<img src="/jspPro/days10/replyboard/images/ico-new.gif" alt="" />
</c:if>
</td>
<td>
<c:if test="${ dto.writer eq 'kenik' }">
<img src="/jspPro/days10/replyboard/images/star.gif" alt="" />
</c:if>
<a href="mailto:${ dto.email }">${ dto.writer }</a>
</td>
<td>${ dto.regdate }</td>
<td>${ dto.readcount }</td>
<td>${ dto.ip }</td>
</tr>
</c:forEach>
<tr>
<td align="center" colspan="6">
<!-- request.setAttribute("pageBlock", "[1] 2 3 4 5 6 7 8 9 10 > ");-->
${ pageBlock }
</td>
</tr>
</c:if>
</tbody>
<form>
<tr>
<td colspan="6" align="center" style="padding:3px;">
<select id="searchCondition" name="searchCondition"
style="font-size: 15px;">
<option value="subject" ${ param.searchCondition eq "subject" ? "selected" : "" }>제목</option>
<option value="writer" ${ param.searchCondition eq "writer" ? "selected" : "" }>작성자</option>
<option value="subject+content">제목+내용</option>
</select>
<input type="text" name="searchWord" value='${ param.searchWord }'>
<input type="button" style="height:22px;width:50px"
value="검색" id="searchBtn">
</td>
</tr>
</form>
</table>
</body>
</html>
view.jsp
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>2022. 6. 27. - 오후 12:16:19</title>
<link rel="shortcut icon" type="image/x-icon" href="../images/SiSt.ico">
<link rel="stylesheet"
href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script
src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<style>
a {
text-decoration: none;
color: black;
}
table {
border-spacing: 1px;
border-collapse: separate;
}
table, tr, td {
border-radius: 3px;
padding: 3px;
}
</style>
<script>
$(document).ready(function (){
$("#btnModalDelete").click(function (){
$("#myModal").modal("show");
});
$("#btnDelete").click(function (){
if( confirm("정말 삭제합니까? ")){
$("#form1").submit();
}
});
});
</script>
</head>
<body>
<h3>days10 - view.jsp</h3>
<table width="600" style="margin: 50px auto" border="1">
<tr>
<td colspan="2" align="right">글보기</td>
</tr>
<tr>
<td width="70" align="center">글번호</td>
<td width="330">${ dto.num }</td>
</tr>
<tr>
<td width="70" align="center">조회수</td>
<td width="330">${ dto.readcount }</td>
</tr>
<tr>
<td width="70" align="center">작성자</td>
<td width="330">${ dto.writer }</td>
</tr>
<tr>
<td width="70" align="center">글제목</td>
<td width="330">${ dto.subject }</td>
</tr>
<tr>
<td width="70" align="center">글내용</td>
<td width="330">
<div style="width: 100%; height: 200px; overflo: scroll;">${ dto.content }
<img src="/jspPro/days10/replyboard/emoticon/이창익.png">
</div>
</td>
</tr>
<tr>
<td colspan="2" align="center"><input type="button" value="글수정"
onclick="location.href='edit.do?num=${ dto.num }&page=${ param.page}&searchCondition=${ param.searchCondition }&searchWord=${ param.searchWord }'">
<a href="delete.do?num=${ dto.num }">글삭제</a> <%--
<input type="button" value="글삭제"
onclick="location.href='Delete.do?num=${ vo.num }¤tPage=${currentPage}'">
--%> <!-- [기억] 답글 버튼 클릭하면 write.do?부모num, 부모그룹 ref, 부모step,부모=depth -->
<input type="button" value="답글"
onclick="location.href='write.do?num=${ dto.num }&ref=${dto.ref }&step=${ dto.step }&depth=${ dto.depth }'">
<input type="button" value="글목록"
onclick="location.href='list.do?page=${ param.page}&searchCondition=${ param.searchCondition }&searchWord=${ param.searchWord }'">
</td>
</tr>
<tr>
<td colspan="2" align="center"><input type="button"
id="btnModalDelete" value="모달창으로 글 삭제"></td>
</tr>
</table>
<!-- 삭제 - 모달창 -->
<div class="modal fade" id="myModal" role="dialog">
<div class="modal-dialog" style="width: 350px">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">게시물 삭제</h4>
</div>
<div class="modal-body">
<!-- Delete.jsp 복사 붙이기. -->
<div style="text-align: center">
<form id="form1" action="delete.do" method="post">
<table width="300px" border="1" align="center">
<tr>
<td>비밀 번호 입력하세요?</td>
</tr>
<tr>
<td><input type="password" name="pass"> <input
type="hidden" name="num" value="${ param.num }"></td>
</tr>
<tr>
<td><input type="button" id="btnDelete" value="글삭제">
<a href="list.do?page=${param.page }">글목록</a></td>
</tr>
</table>
</form>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</body>
</html>
write.jsp
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>2022. 6. 27. - 오전 11:30:23</title>
<link rel="shortcut icon" type="image/x-icon" href="../images/SiSt.ico">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<style>
a{
text-decoration: none;
color:black;
}
table, tr, td {
border-radius: 3px;
}
</style>
</head>
<body>
<h3>days10 - write.jsp</h3>
<form method="post">
<table width="600px" style="margin:50px auto" border="1">
<tr>
<td colspan="2" align="right">
<a href="list.do">글목록</a>
</td>
</tr>
<tr>
<td width="70" align="center">작성자</td>
<td width="330">
<input type="text" name="writer" size="12" >
</td>
</tr>
<tr>
<td width="70" align="center">이메일</td>
<td width="330">
<input type="text" name="email" size="30" >
</td>
</tr>
<tr>
<td width="70" align="center">제목</td>
<td width="330">
<!-- /write.do?num=2&ref=2&step=0&depth=0 부모글 정보 --><!-- 답글일 때는 value 속성으로 찍혀져 있음 -->
<input type="text" name="subject" size="50" value='<c:if test="${ not empty param.ref }">[답글]</c:if>' >
</td>
</tr>
<tr>
<td width="70" align="center">내용</td>
<td width="330">
<textarea rows="13" cols="50" name="content"></textarea>
</td>
</tr>
<tr>
<td width="70" align="center">비밀번호</td>
<td width="330">
<input type="password" name="pass" size="10" >
</td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="submit" value="글쓰기">
<input type="reset" value="다시작성">
<input type="button" value="글목록"
onclick="location.href='list.do'">
</td>
</tr>
</table>
</body>
</html>
[결과]
> 글쓰기 버튼 클릭시
> 글 작성 후 글쓰기 버튼 클릭
> 해당 글 클릭 후
> 답글 버튼 클릭 후 답글 작성 뒤 글쓰기 버튼 클릭
[참고] 답변형(계층형) 게시판 로직 이해
http://taeyo.net/Columns/View.aspx?SEQ=100&PSEQ=9&IDX=1
TAEYO.NET
강좌 목록으로 돌아가기 필자의 잡담~ 이번 강좌는 많은 분들이 기다리던 계층형 게시판입니다. 대상 : ASP.NET을 이용하여 스스로 일반 게시판이 작성가능하거나, Taeyo's ASP.NET v1.0
taeyo.net
'TIL > View Template' 카테고리의 다른 글
[SIST] JSP_days13_Ajax (0) | 2022.06.30 |
---|---|
[SIST] JSP_days11_파일업로드 (0) | 2022.06.28 |
[SIST] JSP_days09_ServletContextListener (0) | 2022.06.27 |
[SIST] JSP_days09_필터(Filter) (0) | 2022.06.27 |
[SIST] JSP_days05/06_게시판 만들기 (0) | 2022.06.27 |