게시글 수정시 첨부파일 없는경우 - 1. 파일추가
게시글 수정시 첨부파일 있는경우 - 1. 변경(기존파일제거) 2. 제거
writer는 member테이블 참조
board_no는 board테이블 참조
comment_ref는 comment테이블 no 참조
컬럼이름이 의미하는바 COMMENTS참고
--댓글 테이블 작성
create table board_comment(
no number,
comment_level number default 1,
writer varchar2(15),
content varchar2(2000),
board_no number,
comment_ref number,
reg_date date default sysdate,
constraint pk_board_comment_no primary key(no),
constraint fk_board_comment_writer foreign key(writer) references member(member_id) on delete set null,
constraint fk_board_comment_board_no foreign key(board_no) references board(no) on delete cascade,
constraint fk_board_comment_ref foreign key(comment_ref) references board_comment(no) on delete cascade
);
comment on column board_comment.no is '게시판댓글번호';
comment on column board_comment.comment_level is '게시판댓글 레벨';
comment on column board_comment.writer is '게시판댓글 작성자';
comment on column board_comment.content is '게시판댓글';
comment on column board_comment.board_no is '참조원글번호';
comment on column board_comment.comment_ref is '게시판댓글 참조번호';
comment on column board_comment.reg_date is '게시판댓글 작성일';
--create sequence
create sequence seq_board_comment_no;
sql developer에서 board_comment 테이블 생성후 내용작성
댓글 대댓글형태의 계층형쿼리 구조예시
start with
: 최상위 행을 지정connect by
: 부모행-자식행의 관계 작성. 부모행의 컬럼 앞에 prior
키워드를 작성select *
from board_comment
start with comment_level = 1
connect by prior no = comment_ref;
--pk/fk관계가 아녀도 가능하지만 보통은 pk/fk에서 사용
order silings by
select lpad(' ',(level - 1) * 5) || content,
level,
bc.*
from board_comment bc
start with comment_level = 1
connect by prior no = comment_ref
order siblings by reg_date desc;
package board.model.vo;
import java.sql.Date;
public class BoardComment {
private int no; //PK
private int commentLevel; //댓글 1, 대댓글 2
private String writer;
private String content;
private int boardNo; //참조게시글
private int commentRef; //대댓글인 경우 참조댓글, 댓글인 경우 null
private Date regDate;
public BoardComment() {
super();
// TODO Auto-generated constructor stub
}
public BoardComment(int no, int commentLevel, String writer, String content, int boardNo, int commentRef,
Date regDate) {
super();
this.no = no;
this.commentLevel = commentLevel;
this.writer = writer;
this.content = content;
this.boardNo = boardNo;
this.commentRef = commentRef;
this.regDate = regDate;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public int getCommentLevel() {
return commentLevel;
}
public void setCommentLevel(int commentLevel) {
this.commentLevel = commentLevel;
}
public String getWriter() {
return writer;
}
public void setWriter(String writer) {
this.writer = writer;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public int getBoardNo() {
return boardNo;
}
public void setBoardNo(int boardNo) {
this.boardNo = boardNo;
}
public int getCommentRef() {
return commentRef;
}
public void setCommentRef(int commentRef) {
this.commentRef = commentRef;
}
public Date getRegDate() {
return regDate;
}
public void setRegDate(Date regDate) {
this.regDate = regDate;
}
@Override
public String toString() {
return "BoardComment [no=" + no + ", commentLevel=" + commentLevel + ", writer=" + writer + ", content="
+ content + ", boardNo=" + boardNo + ", commentRef=" + commentRef + ", regDate=" + regDate + "]";
}
}
BoardComment.java에서 작성
<hr style="margin-top:30px;" />
<div class="comment-container">
<div class="comment-editor">
<form action="<%=request.getContextPath()%>/board/boardCommentInsert" method="post" name="boardCommentFrm">
<input type="hidden" name="boardNo" value="<%= board.getNo() %>" />
<input type="hidden" name="writer" value="<%= loginMember != null ? loginMember.getMemberId() : "" %>" />
<input type="hidden" name="commentLevel" value="1" />
<input type="hidden" name="commentRef" value="0" />
<textarea name="content" cols="60" rows="3"></textarea>
<button type="submit" id="btn-insert">등록</button>
</form>
</div>
<!--table#tbl-comment-->
</div>
boardView.jsp 테이블태그 끝나는곳에 복붙하기
/* 댓글 작성 */
div.comment-container #btn-insert{width:60px; height:50px; color:white; background:#3300ff; position:relative; top:-20px;}
board.css에서 작성에서 작성
로그인하지 않고 댓글작성하려고 할 경우 방지
그 사이에 유효성 검사 추가 - 자바스크립트단의 유효성검사는 선택이 아닌 필수!(서버부하줄여줌)
package board.controller;
import java.io.IOException;
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 board.model.service.BoardService;
import board.model.vo.BoardComment;
/**
* Servlet implementation class BoardCommentInsertServlet
*/
@WebServlet("/board/boardCommentInsert")
public class BoardCommentInsertServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private BoardService boardService = new BoardService();
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
//1. 사용자 입력값 처리
int boardNo = Integer.parseInt(request.getParameter("boardNo"));
int commentLevel = Integer.parseInt(request.getParameter("commentLevel"));
int commentRef = Integer.parseInt(request.getParameter("commentRef"));
String writer = request.getParameter("writer");
String content = request.getParameter("content");
BoardComment bc = new BoardComment(0, commentLevel, writer, content, boardNo, commentRef, null);
System.out.println("boardComment@servlet = " + bc);
//2. 업무로직
int result = boardService.insertBoardComment(bc);
//3. 사용자피드백 & 리다이렉트
request.getSession().setAttribute("msg", "댓글 등록 성공!");
response.sendRedirect(request.getContextPath() + "/board/boardView?no=" + boardNo);
} catch(Exception e) {
e.printStackTrace();
throw e;
}
}
}
BoardCommentInsertServlet.java에서 작성
public int insertBoardComment(BoardComment bc) {
Connection conn = getConnection();
int result = 0;
try {
result = boardDao.insertBoardComment(conn, bc);
commit(conn);
} catch(Exception e) {
rollback(conn);
throw e;
}
return result;
}
BoardService.java에서 작성
board-query.properties에서
insertBoardComment = insert into board_comment(no, comment_level, writer, content, board_no, comment_ref) values(seq_board_comment_no.nextval, ?, ?, ?, ?, ?)
작성
public int insertBoardComment(Connection conn, BoardComment bc) {
String sql = prop.getProperty("insertBoardComment");
int result = 0;
PreparedStatement pstmt = null;
/**
* insert into board_comment
* (no, comment_level, writer, content, board_no, comment_ref)
* values(seq_board_comment_no.nextval, ?, ?, ?, ?, ?)
*/
try {
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, bc.getCommentLevel());
pstmt.setString(2, bc.getWriter());
pstmt.setString(3, bc.getContent());
pstmt.setInt(4, bc.getBoardNo());
// pstmt.setInt(5, bc.getCommentRef() == 0? null : bc.getCommentRef());
//int값에 null을 어케 넣어!
//데이터타입에서 자유로운 Object 타입으로 대입
pstmt.setObject(5, bc.getCommentRef() == 0? null : bc.getCommentRef());
result = pstmt.executeUpdate();
} catch (Exception e) {
throw new BoardException("댓글 등록 오류", e);
} finally {
close(pstmt);
}
return result;
}
BoardDao.java에서 작성
참조하는 키가 없으면 null로 처리해야 하는데
boardView.jsp에서는 기본값을 0으로 처리해놨기 때문에 null로 바꿔야 한다
댓글 테이블에서 댓글 조회 후 jsp에서 뿌리기
BoardViewServlet.java에서 작성
java.util.List import
public List<BoardComment> selectBoardCommentList(int no) {
Connection conn = getConnection();
List<BoardComment> commentList = boardDao.selectBoardCommentList(conn, no);
close(conn);
return commentList;
}
BoardService.java로 이동해서 작성
db에서 가져올 특정게시물(83번)에 대한 댓글 조회
board-query.properties에서
selectBoardCommentList = select bc.* from board_comment bc where board_no = ? start with comment_level = 1 connect by prior no = comment_ref order siblings by reg_date
board_no값 ?(물음표)로 바꾸고 세미콜론지우기
public List<BoardComment> selectBoardCommentList(Connection conn, int no) {
List<BoardComment> commentList = new ArrayList<>();
PreparedStatement pstmt = null;
ResultSet rset = null;
String sql = prop.getProperty("selectBoardCommentList");
try {
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, no);
rset = pstmt.executeQuery();
while(rset.next()) {
BoardComment bc = new BoardComment();
bc.setNo(rset.getInt("no"));
bc.setCommentLevel(rset.getInt("comment_level"));
bc.setWriter(rset.getString("writer"));
bc.setContent(rset.getString("content"));
bc.setBoardNo(rset.getInt("board_no"));
bc.setCommentRef(rset.getInt("comment_ref"));
bc.setRegDate(rset.getDate("reg_date"));
commentList.add(bc);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(rset);
close(pstmt);
}
return commentList;
}
BoardDao.java로 이동해서 작성
List<BoardComment> commentList = (List<BoardComment>) request.getAttribute("commentList");
boardView.jsp로 이동해서
import
작성
<%@page import="board.model.vo.BoardComment"%>
<%@page import="java.util.List"%>
<%@page import="board.model.vo.Board"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ include file="/WEB-INF/views/common/header.jsp" %>
<%
Board board = (Board)request.getAttribute("board");
boolean editable =
loginMember != null &&
(
loginMember.getMemberId().equals(board.getWriter())
|| MemberService.ADMIN_ROLE.equals(loginMember.getMemberRole())
);
List<BoardComment> commentList = (List<BoardComment>) request.getAttribute("commentList");
%>
<link rel="stylesheet" href="<%=request.getContextPath()%>/css/board.css" />
<section id="board-container">
<h2>게시판</h2>
<table id="tbl-board-view">
<tr>
<th>글번호</th>
<td><%= board.getNo() %></td>
</tr>
<tr>
<th>제 목</th>
<td><%= board.getTitle() %></td>
</tr>
<tr>
<th>작성자</th>
<td><%= board.getWriter() %></td>
</tr>
<tr>
<th>조회수</th>
<td><%= board.getReadCount() %></td>
</tr>
<tr>
<th>첨부파일</th>
<td>
<% if(board.getAttach() != null){ %>
<%-- 첨부파일이 있을경우만, 이미지와 함께 original파일명 표시 --%>
<img alt="첨부파일" src="<%=request.getContextPath() %>/images/file.png" width=16px>
<a href="<%= request.getContextPath() %>/board/fileDownload?no=<%= board.getNo() %>"><%= board.getAttach().getOriginalFileName() %></a>
<% } %>
</td>
</tr>
<tr>
<th>내 용</th>
<td><%= board.getContent() %></td>
</tr>
<% if(editable){ %>
<tr>
<%-- 작성자와 관리자만 마지막행 수정/삭제버튼이 보일수 있게 할 것 --%>
<th colspan="2">
<input type="button" value="수정하기" onclick="updateBoard()">
<input type="button" value="삭제하기" onclick="deleteBoard()">
</th>
</tr>
<% } %>
</table>
<hr style="margin-top:30px;" />
<div class="comment-container">
<div class="comment-editor">
<form action="<%=request.getContextPath()%>/board/boardCommentInsert" method="post" name="boardCommentFrm">
<input type="hidden" name="boardNo" value="<%= board.getNo() %>" />
<input type="hidden" name="writer" value="<%= loginMember != null ? loginMember.getMemberId() : "" %>" />
<input type="hidden" name="commentLevel" value="1" />
<input type="hidden" name="commentRef" value="0" />
<textarea name="content" cols="60" rows="3"></textarea>
<button type="submit" id="btn-insert">등록</button>
</form>
</div>
<!--table#tbl-comment-->
<% if(commentList != null && !commentList.isEmpty()){ %>
<table id="tbl-comment">
<%
for(BoardComment bc : commentList){
boolean removable =
loginMember != null &&
(
loginMember.getMemberId().equals(bc.getWriter())
|| MemberService.ADMIN_ROLE.equals(loginMember.getMemberRole())
);
if(bc.getCommentLevel() == 1){
//댓글
%>
<tr class="level1">
<td>
<sub class="comment-writer"><%= bc.getWriter() %></sub>
<sub class="comment-date"><%= bc.getRegDate() %></sub>
<br />
<%= bc.getContent() %>
</td>
<td>
<button class="btn-reply" value="<%= bc.getNo() %>">답글</button>
<% if(removable){ %>
<button class="btn-delete" value="<%= bc.getNo() %>">삭제</button>
<% } %>
</td>
</tr>
<%
} else {
//대댓글
%>
<tr class="level2">
<td>
<sub class="comment-writer"><%= bc.getWriter() %></sub>
<sub class="comment-date"><%= bc.getRegDate() %></sub>
<br />
<%= bc.getContent() %>
</td>
<td>
<% if(removable){ %>
<button class="btn-delete" value="<%= bc.getNo() %>">삭제</button>
<% } %>
</td>
</tr>
<%
}
}
%>
</table>
<% } %>
</div>
</section>
/*댓글테이블*/
table#tbl-comment {width:580px; margin:0 auto; border-collapse:collapse; }
table#tbl-comment tr td{border-bottom:1px solid; border-top:1px solid; padding:5px; text-align:left; line-height:120%;}
table#tbl-comment tr td:first-of-type {padding: 5px 5px 5px 50px;}
table#tbl-comment tr td:last-of-type {text-align:right; width: 100px;}
table#tbl-comment button.btn-reply{display:none;}
table#tbl-comment tr:hover {background:lightgray;}
table#tbl-comment tr:hover button.btn-reply{display:inline;}
table#tbl-comment sub.comment-writer {color:navy; font-size:14px}
table#tbl-comment sub.comment-date {color:tomato; font-size:10px}
table#tbl-comment tr.level2 {color:gray; font-size: 14px;}
table#tbl-comment tr.level2 td:first-of-type{padding-left:100px;}
table#tbl-comment tr.level2 sub.comment-writer {color:#8e8eff; font-size:14px}
table#tbl-comment tr.level2 sub.comment-date {color:#ff9c8a; font-size:10px}
/*답글관련*/
table#tbl-comment textarea{margin: 4px 0 0 0;}
table#tbl-comment button.btn-insert-reply {width:60px; height:23px; color:white; background:#3300ff; position:relative; top:-5px; left:10px;}
/* 삭제버튼관련 */
table#tbl-comment button.btn-delete{background:red; color:white; display:none;}
table#tbl-comment tr:hover button.btn-delete{display:inline;}
board.css으로 이동해서 작성. 꾸며주기!
이렇게 나옴.
boardView.jsp에서 이벤트 핸들러(클릭시 댓글작성 폼생성) 작성
$(".btn-reply").click(function(){
<% if(loginMember == null){ %>
loginAlert();
return;
<% } %>
//대댓글 작성폼 동적 생성
var html = "<tr>";
html += "<td colspan='2' style='display:none; text-align:left;'>";
html += '<form action="<%=request.getContextPath()%>/board/boardCommentInsert" method="post" name="boardCommentFrm">';
html += '<input type="hidden" name="boardNo" value="<%= board.getNo() %>" />';
html += '<input type="hidden" name="writer" value="<%= loginMember != null ? loginMember.getMemberId() : "" %>" />';
html += '<input type="hidden" name="commentLevel" value="2" />';
html += '<input type="hidden" name="commentRef" value="' + $(this).val() + '" />';
html += '<textarea name="content" cols="60" rows="2"></textarea>';
html += '<button type="submit" class="btn-insert-reply">등록</button>';
html += '</form>';
html += "</td>";
html += "</tr>";
var $trOfBtn = $(this).parent().parent();
$(html)
.insertAfter($trOfBtn)
.children("td")
.slideDown(800);
//버튼은 1회용처리
$(this).off("click");
});
코드안에서는 안보이지만 스크립트 안에서 작성해야함.
형제 태그로 대댓글 작성 폼 삽입
대댓글 입력창은 동적으로 생성됐기 때문에 정규표현식 처리하는 이벤트핸들러가 잡지 못한다.
why? 이벤트핸들러가 있을때는 댓글폼이 없어서 폼의 이름이 같아도 유효성 검사를 하지 못함.
해결법
1. 일일히 정규표현식 넣기
2. eventBubling처리
부모(현재는 최상위객체인 document로처리함)에서 bubling해서 자식의 이벤트처리시킴.
//form submit 시
//부모에서 자식의 이벤트를 처리
$(document).on('submit', '[name=boardCommentFrm]', function(e){
//$(document.boardCommentFrm).submit(function(){
<% if(loginMember == null){ %>
loginAlert();
return false;
<% } %>
//댓글내용
var $content = $("[name=content]", e.target);
if(/^(.|\n)+$/.test($content.val()) == false){
alert("댓글 내용을 작성하세요.");
$content.focus();
return false;
}
//javascript에서 유효성검사는 필수다.
});
어느페이지에 머물고있었는지 알아내서 그리로 보내면됨.
개발자도구가서 Network-login-Request Headers가보면 Referer에 마지막 머물던 페이지 확인가능
http://localhost:9090/mvc/board/boardView?no=83
MemberLoginServlet.java로 이동해서 작성
//이전페이지로 리다이렉트 처리
String referer = request.getHeader("Referer");
System.out.println("referer@servlet = " + referer);
//리다이렉트 : url변경
response.sendRedirect(referer);
작성자랑 관리자만 삭제가능하도록 설정(삭제버튼이 작성자랑 관리자만 보이도록!)
boardView.jsp에서
boolean removable =
loginMember != null && (loginMember.getMemberId().equals(bc.getWriter()) || emberService.ADMIN_ROLE.equals(loginMember.getMemberRole()));
boolean타입 removable 만들어주기
<td>
<button class="btn-reply" value="<%= bc.getNo() %>">답글</button>
<% if(removable){ %>
<button class="btn-delete" value="<%= bc.getNo() %>">삭제</button>
<% } %>
</td>
removable이 true일 경우에만 삭제버튼이 보이도록!
level1 & level2 모두 적용시키기
form을 만들어서 처리해주어야함.
$(".btn-delete").click(function(){
if(confirm("해당 댓글을 삭제하시겠습니까?")){
var $frm = $(document.boardCommentDelFrm);
var boardCommentNo = $(this).val();
$frm.find("[name=no]").val(boardCommentNo);
$frm.submit();
}
});
삭제버튼을 클릭했을 경우 event핸들러 만들어주기
package board.controller;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
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 board.model.service.BoardService;
/**
* Servlet implementation class BoardCommentDeleteServlet
*/
@WebServlet("/board/boardCommentDelete")
public class BoardCommentDeleteServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private BoardService boardService = new BoardService();
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
//1. 파라미터값 가져오기
int no = Integer.parseInt(request.getParameter("no"));
int boardNo = Integer.parseInt(request.getParameter("boardNo"));
//2. 비지니스로직 호출
int result = boardService.deleteBoardComment(no);
//3. 사용자피드백 & 리다이렉트
request.getSession().setAttribute("msg", "댓글 삭제 성공!");
response.sendRedirect(request.getContextPath() + "/board/boardView?no=" + boardNo);
} catch(Exception e) {
e.printStackTrace();
throw e;
}
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
BoardCommentDeleteServlet.java만들어서 작성해주기
public int deleteBoardComment(int no) {
Connection conn = getConnection();
int result = 0;
try {
result = boardDao.deleteBoardComment(conn, no);
commit(conn);
} catch(Exception e) {
rollback(conn);
throw e;
}
return result;
}
BoardService.java에서 작성
deleteBoardComment = delete from board_comment where no = ?
board-query.propertiesd에서 작성
public int deleteBoardComment(Connection conn, int no) {
PreparedStatement pstmt = null;
int result = 0;
String sql = prop.getProperty("deleteBoardComment");
try {
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, no);
result = pstmt.executeUpdate();
} catch (SQLException e) {
throw new BoardException("댓글 삭제 오류", e);
} finally {
close(pstmt);
}
return result;
}
BoardDao.java에서 작성
BoardExt.java 작성
if(list != null && !list.isEmpty()) {
for(Board board : list){
BoardExt b = (BoardExt) board;
<a href="<%= request.getContextPath() %>/board/boardView?no=<%= b.getNo() %>"><%= b.getTitle() %><%= b.getCommentCnt() > 0 ? "(" + b.getCommentCnt() + ")" : "" %></a>
boardList.jsp에서 작성하고
import해주기