일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- date
- priority_queue
- javascript
- map
- 힙덤프
- NIO
- math
- JPA
- 리소스모니터링
- Calendar
- 큐
- scanner
- deque
- CSS
- spring boot
- string
- List
- sql
- alter
- BFS
- 스프링부트
- html
- 스택
- set
- Java
- dfs
- Union-find
- GC로그수집
- union_find
- Properties
- Today
- Total
매일 조금씩
Spring MVC 로 댓글(답글) 구현하기 본문
사진자랑 게시판(게시판명: picture)의 '댓글' 기능을 예로 들었다.
picture 게시판에선 카드리스트에서 카드를 클릭하면 나오는 모달창에서 댓글 아이콘을 클릭해야
댓글들을 볼 수 있고, 댓글과 대댓글 작성이 가능하다.
자신이 작성한 댓글은 인스타그램처럼 수정은 불가능하고 삭제만 가능하다.
*** 순서 ***
1. 구현 완료 모습
2. DB 테이블 구축
3. 코드
3-1. JSP
3-2. Controller
3-3. DAO
3-4. TO
3-5. SQL(mapper)
1. 구현 완료 모습
모달창에서 댓글 아이콘을 클릭해야 댓글을 볼 수 있고, 댓글 작성이 가능하다.
다음은 답글 작성 과정인데 댓글 작성도 이와 유사하다.
내가 작성한 댓글은 삭제 버튼이 나온다.
2. DB 테이블 구축
#사진자랑 게시판 댓글 테이블(p_reply)
create table p_reply(
no int auto_increment primary key,
bno int not null,
grp int not null,
grps int not null,
grpl int not null,
writer varchar(100) not null,
content varchar(1000),
wdate datetime not null,
constraint p_r_writer_fk foreign key(writer) references user(nick)
on delete cascade on update cascade,
constraint p_r_bno_fk foreign key(bno) references p_board(no)
on delete cascade
);
순번 |
컬럼명 |
컬럼설명 |
데이터 타입 |
Null여부 |
제약조건 |
0 |
no |
댓글 번호 |
int |
not null |
pk auto_increment |
1 |
bno |
게시물 번호 |
int |
not null |
fk references p_board(no) on delete cascade |
2 |
grp |
댓글이 속한 댓글 번호 |
int |
not null |
|
3 |
grps | 같은 grp 중에 순서 |
int |
not null |
|
4 |
grpl |
댓글의 깊이 |
int |
not null |
|
5 |
writer |
작성자의 nick |
varchar(100) |
not null |
fk references user(nick) on delete cascade on update cascade |
6 |
content |
댓글 작성 내용 |
varchar(1000) |
|
|
7 |
wdate |
댓글 작성 시간 |
datetime |
not null |
foreign key인 bno와 writer에 대해 제약조건을 걸었다.
1. 게시물(bno) 제약조건 p_r_bno_fk
- 게시물이 삭제되면 댓글도 사라진다. on delete cascade
2. 작성자(writer) 제약조건 p_r_writer_fk
- 작성자가 삭제되면(탈퇴) 댓글도 사라진다. on delete cascade
- 작성자의 닉네임이 수정되면 writer도 수정된다. on update cascade
3. 코드
3-1. JSP
> view > picture > ajax_page.jsp
▶ 댓글아이콘 클릭시 댓글리스트를 보이는 부분 html
<!-- 댓글 -->
<div class="collapse" id="reply_card${tmp.no }">
<section class="modal-section">
<div class="card card-body">
<!-- 댓글 목록 -->
<div class="reply-list reply-list${tmp.no }">
<!-- 댓글이 목록이 들어가는 곳 -->
</div>
<!-- 댓글 작성 => 로그인한 상태여야만 댓글작성 칸이 나온다. -->
<c:if test="${not empty sessionScope.nick }">
<div class="row reply_write">
<div class="col-1">
<a href="other_profile.do?other_nick=${tmp.writer }">
<img id="write_reply_profileImage"
src="./upload/profile/${sessionScope.profile }" />
</a>
</div>
<div class="col-8" class="input_reply_div">
<input class="w-100 form-control" id="input_reply${tmp.no}"
type="text" placeholder="댓글입력...">
</div>
<div class="col-3 ">
<button type="button" idx="${tmp.no }"
class="btn btn-success mb-1 write_reply">댓글 달기</button>
</div>
</div>
</c:if>
</div>
</section>
</div>
위 코드에서 아래 태그 안에 댓글 리스트가 들어가게 된다.
<!-- 댓글 목록 -->
<div class="reply-list reply-list${tmp.no }">
<!-- 댓글이 목록이 들어가는 곳 -->
</div>
▶ 댓글아이콘 클릭시 댓글리스트를 가져와서 삽입하는 javascript
// [댓글]
// 게시물의 댓글 목록을 불러오는 함수입니다.
const ReplyList = function(no) {
$.ajax({
url : 'picture_replyList.do',
type : 'get',
data : {
no : no
},
success : function(data) {
console.log("댓글 리스트 가져오기 성공");
// 댓글 목록을 html로 담기
let listHtml = "";
for(const i in data){
let no = data[i].no;
let bno = data[i].bno;
let grp = data[i].grp;
let grps = data[i].grps;
let grpl = data[i].grpl;
let writer = data[i].writer;
let content = data[i].content;
let wdate = data[i].wdate;
let wgap = data[i].wgap;
let profile = data[i].profile;
console.log(grpl); // 모댓글일땐 0, 답글일땐 1
listHtml += "<div class='row replyrow reply" + no + "'>";
if(content == ""){ // 삭제된 댓글일때
listHtml += " <div>";
listHtml += " (삭제된 댓글입니다)";
listHtml += " </div>";
}else{
if(grpl == 0){ // 모댓글일때
listHtml += " <div class='col-1'>";
listHtml += " <a href='other_profile.do?other_nick="+writer+"'> ";
listHtml += " <img class='reply_list_profileImage' src='./upload/profile/"+ profile +"'/>";
listHtml += " </a> ";
listHtml += " </div>";
listHtml += " <div class='rereply-content col-8'>";
listHtml += " <div>";
listHtml += " <span>";
listHtml += " <b>"+ writer +"</b>";
listHtml += " </span>";
listHtml += " <span>";
listHtml += content;
listHtml += " </span>";
listHtml += " </div>";
// 현재 로그인 상태일때 답글작성 버튼이 나온다.
if("${nick}" != ""){
listHtml += " <div>";
// 함수에 게시글번호(bno), 모댓글번호(no), 모댓글 작성자(writer)를 인자로 담아서 넘긴다.
// 이때 모댓글 작성자 writer는 string인데 string을 인자에 넣기 위해선''나""로 감싸줘야한다.
// 여기선 ''와 ""가 이미 둘다 쓰이고 있는데 href를 감싸고 있는 ''와 겹치지 않는 ""를 \" 처리해서 넣어줬다.
listHtml += " <a href='#' class='write_reply_start' data-bs-toggle='collapse' data-bs-target='#re_reply"+ no +"' aria-expanded='false' aria-controls='collapseExample'>답글 달기</a>";
listHtml += " </div>";
}
listHtml += " </div>";
}else{ // 답글일때
listHtml += " <div class='col-1'>"
listHtml += " </div>"
listHtml += " <div class='col-1'>";
listHtml += " <img class='reply_list_profileImage' src='./upload/profile/"+ profile +"'/>";
listHtml += " </div>";
listHtml += " <div class='rereply-content"+ no +" col-7'>";
listHtml += " <div>";
listHtml += " <span>";
listHtml += " <b>"+ writer +"</b>";
listHtml += " </span>";
listHtml += " <span>";
listHtml += content;
listHtml += " </span>";
listHtml += " </div>";
listHtml += " </div>";
}
listHtml += " <div class='col-3 reply-right'>";
listHtml += " <div>";
listHtml += wdate;
listHtml += " </div>";
// 책갈피
// 현재 로그인 상태이고..
if("${nick}" != ""){
//현재 사용자가 이 댓글의 작성자일때 삭제 버튼이 나온다.
if("${nick}" == writer){
listHtml += " <div>";
// 수정할 댓글의 no를 grpl과 함께 넘긴다.
// 모댓글 수정칸과 답글 수정칸을 화면에 다르게 나타내야하기 때문에 모댓글과 답글을 구분하는 grpl을 함께 넘겨주어야한다.
//listHtml += " <a href='javascript:' no='"+ no +"' grpl='"+ grpl +"' class='reply_modify'>수정</a>";
//listHtml += " | ";
// 삭제는 no만 넘겨주면 된다.
listHtml += " <a href='javascript:' no='"+ no +"' grpl='"+ grpl + "' bno='"+ bno +"' grp='"+ grp +"' class='reply_delete'>삭제</a>";
listHtml += " </div>";
}
}
listHtml += " </div>";
// 댓글에 답글달기를 누르면 답글입력란이 나온다.
// ---- 답글입력란
listHtml += " <div class='collapse row rereply_write' id='re_reply"+ no +"'>";
listHtml += " <div class='col-1'>"
listHtml += " </div>"
listHtml += " <div class='col-1'>"
listHtml += " <a href='other_profile.do?other_nick="+writer+"'> ";
listHtml += " <img id='write_reply_profileImage' src='./upload/profile/${profile}'/>"
listHtml += " </a> ";
listHtml += " </div>"
listHtml += " <div class='col-7'>"
listHtml += " <input class='w-100 input_rereply_div form-control' id='input_rereply"+ no +"' type='text' placeholder='댓글입력...'>"
listHtml += " </div>"
listHtml += " <div class='col-3'>"
// 답글달기 버튼이 눌리면 모댓글 번호(no)와 게시물번호(bno)를 함수에 전달한다.
// 동적으로 넣은 html태그에서 발생하는 이벤트는 동적으로 처리해줘야한다 !!!!!
// 예를들어, 동적으로 넣은 html태그에서 발생하는 click 이벤트는 html태그 안에서 onclick으로 처리하면 안되고, jquery에서 클래스명이나 id값으로 받아서 처리하도록 해야한다.
// 아래코드를 보자~~~~
// listHtml += " <button onclick='javascript:WriteReReply("+ no +","+ bno +")' type='button' class='btn btn-success mb-1 write_rereply' >답글 달기</button>"
// 위 코드는 클릭되어도 값이 넘겨지지 않는다. 값이 undefined가 된다.
// 아래코드처럼 짜야한다. click이벤트를 처리하지 않고 데이터(no, bno)만 속성으로 넘겨주도록 작성한다.
listHtml += " <button type='button' class='btn btn-success mb-1 write_rereply' no='" + no + "' bno='" + bno + "'>답글 달기</button>"
listHtml += " </div>";
listHtml += " </div>";
// ---- 답글입력란 끝
}
listHtml += "</div>";
};
///////////// 동적으로 넣어준 html에 대한 이벤트 처리는 같은 함수내에서 다 해줘야한다.
///////////// $(document).ready(function(){}); 안에 써주면 안된다.
// 댓글 리스트 부분에 받아온 댓글 리스트를 넣기
$(".reply-list"+no).html(listHtml);
// 답글에서 답글달기를 누르면 input란에 "@답글작성자"가 들어간다.
//$('.write_re_reply_start').on('click', function(){
// $('#input_rereply'+ $(this).attr('no')).val("@"+$(this).attr('writer')+" ");
//});
//답글을 작성한 후 답글달기 버튼을 눌렀을 때 그 click event를 아래처럼 jquery로 처리한다.
$('button.btn.btn-success.mb-1.write_rereply').on( 'click', function() {
console.log( 'no', $(this).attr('no') );
console.log( 'bno', $(this).attr('bno') );
// 답글을 DB에 저장하는 함수를 호출한다. bno와 no를 같이 넘겨주어야한다.
WriteReReply($(this).attr('bno'), $(this).attr('no') );
});
// 삭제버튼을 클릭했을 때
$('.reply_delete').on('click', function(){
// 모댓글 삭제일때
if($(this).attr('grpl') == 0){
DeleteReply($(this).attr('no'), $(this).attr('bno'));
// 답글 삭제일때
}else{
DeleteReReply($(this).attr('no'), $(this).attr('bno'), $(this).attr('grp'));
}
})
},
error : function() {
alert('서버 에러');
}
});
};
위 코드를 보면
아래처럼 댓글을 html 형식으로 listHtml 에 담아서 html()로 댓글이 들어갈 부분에 넣는다.
// 댓글 리스트 부분에 받아온 댓글 리스트를 넣기
$(".reply-list"+no).html(listHtml);
위에서 답글이있는 모댓글이 삭제된 경우의 코드 부분이다.
댓글 내용이 공백 ""인 경우엔 아래처럼 (삭제된 댓글입니다)를 출력하도록 구현했다. 아래와같이 구현된다.
댓글 내용이 공백 ""인 경우는 이렇게 답글이 있는데 삭제된 모댓글인 경우 밖에 없다.
댓글, 답글달기 버튼 클릭시 아무것도 입력되지 않으면 작성되지 않게 구현했기 때문이다.
또한, 여기서 ajax로 동적으로 삽입되는 html인 listHtml에 대한 이벤트 처리는 ajax문 안에 같이 써줘야한다. 아니면 안먹는다.
다음 코드는 그 이벤트 처리문이다.
//답글을 작성한 후 답글달기 버튼을 눌렀을 때 그 click event를 아래처럼 jquery로 처리한다.
$('button.btn.btn-success.mb-1.write_rereply').on( 'click', function() {
console.log( 'no', $(this).attr('no') );
console.log( 'bno', $(this).attr('bno') );
// 답글을 DB에 저장하는 함수를 호출한다. bno와 no를 같이 넘겨주어야한다.
WriteReReply($(this).attr('bno'), $(this).attr('no') );
});
// 삭제버튼을 클릭했을 때
$('.reply_delete').on('click', function(){
// 모댓글 삭제일때
if($(this).attr('grpl') == 0){
DeleteReply($(this).attr('no'), $(this).attr('bno'));
// 답글 삭제일때
}else{
DeleteReReply($(this).attr('no'), $(this).attr('bno'), $(this).attr('grp'));
}
});
▶ 필요한 함수들 javascript
// 답글 달기 버튼 클릭시 실행 - 답글 저장, 댓글 갯수 가져오기
const WriteReReply = function(bno,no) {
console.log(bno);
console.log(no);
console.log($("#input_rereply" + no).val());
// 댓글 입력란의 내용을 가져온다.
// ||"" 를 붙인 이유 => 앞뒤 공백을 제거한다.(띄어쓰기만 입력했을때 댓글작성안되게 처리하기위함)
let content = $("#input_rereply" + no).val();
content = content.trim();
if(content == ""){ // 입력된게 없을때
alert("글을 입력하세요!");
}else{
// 입력란 비우기
$("#input_rereply" + no).val("");
// reply+1 하고 그 값을 가져옴
$.ajax({
url : 'picture_write_rereply.do',
type : 'get',
data : {
no : no,
bno : bno,
content: content
},
success : function(pto) {
let reply = pto.reply;
// 페이지, 모달창에 댓글수 갱신
$('#m_reply'+bno).text(reply);//
$('#reply'+bno).text(reply);
console.log("답글 작성 성공");
// 게시물 번호(bno)에 해당하는 댓글리스트를 새로 받아오기
ReplyList(bno);
},
error : function() {
alert('서버 에러');
}
});
};
};
// 모댓글 삭제일때
const DeleteReply = function(no, bno){
// grp이 no인 댓글이 있는 경우 content에 null을 넣고 없으면 삭제한다.
$.ajax({
url : 'picture_delete_reply.do',
type : 'get',
data : {
no : no,
bno : bno
},
success : function(pto) {
let reply = pto.reply;
// 페이지, 모달창에 댓글수 갱신
$('#m_reply'+bno).text(reply);
$('#reply'+bno).text(reply);
console.log("모댓글 삭제 성공");
// 게시물 번호(bno)에 해당하는 댓글리스트를 새로 받아오기
ReplyList(bno);
},
error : function() {
alert('서버 에러');
}
});
};
// 답글 삭제일때
const DeleteReReply = function(no, bno, grp){
//console.log("grp : " + grp);
// 답글을 삭제한다.
$.ajax({
url : 'picture_delete_rereply.do',
type : 'get',
data : {
no : no,
bno : bno,
grp : grp
},
success : function(pto) {
let reply = pto.reply;
// 페이지, 모달창에 댓글수 갱신
$('#m_reply'+bno).text(reply);
$('#reply'+bno).text(reply);
console.log("답글 삭제 성공");
// 게시물 번호(bno)에 해당하는 댓글리스트를 새로 받아오기
ReplyList(bno);
},
error : function() {
alert('서버 에러');
}
});
};
3-2. Controller
> PictureController.java
// 모댓글 작성
@ResponseBody
@RequestMapping(value = "/picture_write_reply.do")
public PictureTO write_reply(@RequestParam String no, @RequestParam String content, HttpSession session) {
ReplyTO to = new ReplyTO();
// 게시물 번호 세팅
to.setBno(no);
// 댓글 내용 세팅
to.setContent(content);
// 댓글작성자 nick을 writer로 세팅
to.setWriter((String) session.getAttribute("nick"));
// +1된 댓글 갯수를 담아오기 위함
PictureTO pto = replyDao.pictureWriteReply(to);
return pto;
}
// 답글 작성
@ResponseBody
@RequestMapping(value = "/picture_write_rereply.do")
public PictureTO write_rereply(@RequestParam String no, @RequestParam String bno, @RequestParam String content,
HttpSession session) {
ReplyTO to = new ReplyTO();
// 게시물 번호 세팅
to.setBno(bno);
// grp, grps, grpl 은 ReplyTO에 int로 정의되어 있기 때문에 String인 no를 int로 변환해서 넣어준다.
// 모댓글 번호 no를 grp으로 세팅한다.
to.setGrp(Integer.parseInt(no));
// 답글은 깊이가 1이되어야 하므로 grpl을 1로 세팅한다.
to.setGrpl(1);
// 답글 내용 세팅
to.setContent(content);
// 답글작성자 nick을 writer로 세팅
to.setWriter((String) session.getAttribute("nick"));
// +1된 댓글 갯수를 담아오기 위함
PictureTO pto = replyDao.pictureWriteReReply(to);
return pto;
}
// 댓글 리스트
@ResponseBody
@RequestMapping(value = "/picture_replyList.do")
public ArrayList<ReplyTO> reply_list(@RequestParam String no, HttpSession session) {
ReplyTO to = new ReplyTO();
// 가져올 댓글 리스트의 게시물번호를 세팅
to.setBno(no);
ArrayList<ReplyTO> replyList = new ArrayList();
replyList = replyDao.replyList(to);
return replyList;
}
// 모댓글 삭제
@ResponseBody
@RequestMapping(value = "/picture_delete_reply.do")
public PictureTO picture_delete_reply(@RequestParam String no, @RequestParam String bno ) {
ReplyTO to = new ReplyTO();
// 모댓글 번호 세팅
to.setNo(no);
// 게시물 번호 세팅
to.setBno(bno);
// 갱신된 댓글 갯수를 담아오기 위함
PictureTO pto = replyDao.pictureDeleteReply(to);
return pto;
}
// 답글 삭제
@ResponseBody
@RequestMapping(value = "/picture_delete_rereply.do")
public PictureTO delete_rereply(@RequestParam String no, @RequestParam String bno, @RequestParam int grp) {
ReplyTO to = new ReplyTO();
// 답글 번호 세팅 - 답글 삭제하기 위해서 필요함
to.setNo(no);
// 게시물 번호 세팅 - p_board 의 reply+1하기 위해 필요함
to.setBno(bno);
// grp 세팅(모댓글이 뭔지) - 모댓글은 삭제를 해도 답글이 있으면 남아있게 되는데 답글이 모두 삭제되었을 때 모댓글도 삭제하기 위해
// 필요함
to.setGrp(grp);
// 갱신된 댓글 갯수를 담아오기 위함
PictureTO pto = replyDao.pictureDeleteReReply(to);
return pto;
}
3-3. DAO
> PictureReplyDAO.java
package com.exam.model1.pictureReply;
import java.util.ArrayList;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.exam.model1.picture.PictureTO;
import com.exam.model1.pictureHeart.PictureHeartTO;
import com.exam.model1.pictureReply.ReplyTO;
@Repository
public class ReplyDAO {
@Autowired
private SqlSession sqlSession;
// 댓글 작성
public PictureTO pictureWriteReply(ReplyTO to) {
// p_board 테이블에 해당 게시물의 reply수를 +1 하기위한 to세팅
PictureTO pto = new PictureTO();
pto.setNo(to.getBno());
// 해당 게시물의 reply를 +1 한다.
sqlSession.update("picture_reply_up", pto);
// 현재 p_reply 테이블의 가장 큰 no값을 가져온다.
int grp = sqlSession.selectOne("p_reply_max_no");
// grp 세팅
to.setGrp(grp+1);
int result = sqlSession.insert("picture_reply_write", to);
int check = sqlSession.selectOne("p_reply_max_no");
// grp를 현재 가장 큰 no 즉 방금 넣은 데이터의 no값로 세팅함
to.setGrp(check);
// no 와 grp가 다르면 grp를 no로 없데이트
int check_update = sqlSession.update("picture_reply_check", to);
if (result == 1) { // p_reply 테이블에 새로운 댓글 추가가 성공한다면..
// 갱신된 댓글 갯수를 가져옴
pto = sqlSession.selectOne("picture_reply_count", pto);
}
return pto;
}
// 답글 작성
public PictureTO pictureWriteReReply(ReplyTO to) {
// p_board 테이블에 해당 게시물의 reply수를 +1 하기위한 to세팅
PictureTO pto = new PictureTO();
pto.setNo(to.getBno());
// 해당 게시물의 reply를 +1 한다.
sqlSession.update("picture_reply_up", pto);
// p_reply 테이블에 추가 (댓글 작성과 동일)
int result = sqlSession.insert("picture_rereply_write", to);
if (result == 1) { // p_reply 테이블에 새로운 댓글 추가가 성공한다면..
// 갱신된 댓글 갯수를 가져옴
pto = sqlSession.selectOne("picture_reply_count", pto);
}
return pto;
}
// 댓글 리스트
public ArrayList<ReplyTO> replyList(ReplyTO to){
ArrayList<ReplyTO> replyList = new ArrayList();
replyList = (ArrayList)sqlSession.selectList("picutre_replyList", to);
return replyList;
}
// 모댓글 삭제
public PictureTO pictureDeleteReply(ReplyTO to) {
// p_board 테이블에 해당 게시물의 reply수를 -1 하기위한 to세팅
PictureTO pto = new PictureTO();
pto.setNo(to.getBno());
// grp가 reply의 no와 일치하는 댓글이 몇갠지 카운트한다. 모댓글에 딸린 답글이 몇갠지 카운트하기 위함
int count_rereply = sqlSession.selectOne("picture_count_rereply", to);
int result = 0;
// 해당 게시물의 reply를 -1 한다.
sqlSession.update("picture_reply_down", pto);
if(count_rereply==0) { // 답글이 없을 때 - 그냥 삭제
// p_reply 테이블에서 삭제
result = sqlSession.delete("picture_reply_delete", to);
}else { // 답글이 있을 때 - content에 공백을 넣음 ("삭제된 게시물입니다" 라고 표기하기 위함)
// p_reply 테이블에서 삭제하지 않고 content에 공백을 넣음
result = sqlSession.update("picture_reply_not_delete", to);
}
if (result == 1) { // p_reply 테이블에서 댓글삭제가 성공한다면..
// 갱신된 댓글 갯수를 가져옴
pto = sqlSession.selectOne("picture_reply_count", pto);
}
return pto;
}
// 답글 삭제
public PictureTO pictureDeleteReReply(ReplyTO to) {
// p_board 테이블에 해당 게시물의 reply수를 -1 하기위한 to세팅
PictureTO pto = new PictureTO();
pto.setNo(to.getBno());
// 해당 게시물의 reply를 -1 한다.
sqlSession.update("picture_reply_down", pto);
// p_reply 테이블에서 삭제
int result = sqlSession.delete("picture_reply_delete", to);
// grp가 일치하는 답글이 몇갠지 카운트 한다. 없고 모댓글의 content가 ""이면 모댓글을 삭제하기 위함.
int count_rereply = sqlSession.selectOne("picture_count_rereply_fromrereply", to);
System.out.println("count_rereply = " + count_rereply);
if(count_rereply == 0) {
sqlSession.delete("picture_reply_delete_after_rereply_delete", to);
}
if (result == 1) { // p_reply 테이블에서 댓글삭제가 성공한다면..
// 갱신된 댓글 갯수를 가져옴
pto = sqlSession.selectOne("picture_reply_count", pto);
}
return pto;
}
// 댓글 작성
public PictureTO profile_pictureWriteReply(ReplyTO to) {
// p_board 테이블에 해당 게시물의 reply수를 +1 하기위한 to세팅
PictureTO pto = new PictureTO();
pto.setNo(to.getBno());
// 해당 게시물의 reply를 +1 한다.
sqlSession.update("picture_reply_up", pto);
// 현재 p_reply 테이블의 가장 큰 no값을 가져온다.
int grp = sqlSession.selectOne("p_reply_max_no");
// grp 세팅
to.setGrp(grp+1);
int result = sqlSession.insert("picture_reply_write", to);
int check = sqlSession.selectOne("p_reply_max_no");
// grp를 현재 가장 큰 no 즉 방금 넣은 데이터의 no값로 세팅함
to.setGrp(check);
// no 와 grp가 다르면 grp를 no로 없데이트
//int check_update = sqlSession.update("picture_reply_check", to);
if (result == 1) { // p_reply 테이블에 새로운 댓글 추가가 성공한다면..
// 갱신된 댓글 갯수를 가져옴
pto = sqlSession.selectOne("picture_reply_count", pto);
}
return pto;
}
}
답글은 삭제시 바로 삭제 된다.
이때 모댓글이 삭제된 상태이고 마지막 답글인 경우 모댓글인 (삭제된 댓글입니다)와 함께 삭제되며,
DB에서도 그제서야 모댓글을 완전히 삭제한다.
댓글은 삭제가 실행될 때마다, 댓글에 달린 답글의 수를 가져온다.
답글수가
0이면 바로 삭제되고,
1 이상이면, 삭제 되지 않고 DB 테이블(여기선 p_reply 테이블)의 content 컬럼에 공백 ""이 저장되도록 구현하였다.
content가 공백""이면 댓글리스트에 (삭제된 댓글입니다)를 출력하는건 프론트 javascript에서 처리한다.
3-4. TO
> PictureReplyTO.java
package com.exam.model1.pictureReply;
public class ReplyTO {
private String no;
private String bno; // 댓글이 속한 게시글 번호 (받아와야 하는 값)
private int grp; // 댓글 그룹 번호 (모댓글과 거기에 속한 대댓글은 같은 grp를 가짐)
private int grps; // 그룹 내 댓글 순서 (오래된글 ~ 최신글 오름차순)
private int grpl; // 그룹내 댓글 깊이(댓글인지 대댓글인지)
private String writer;
private String content;
private String wdate;
// wdate과 현재시간의 차이를 계산후 받아오기 위함
private String wgap;
// writer의 프로필사진을 가져온다.
private String profile;
public String getWgap() {
return wgap;
}
public void setWgap(String wgap) {
this.wgap = wgap;
}
public String getProfile() {
return profile;
}
public void setProfile(String profile) {
this.profile = profile;
}
public String getNo() {
return no;
}
public void setNo(String no) {
this.no = no;
}
public String getBno() {
return bno;
}
public void setBno(String bno) {
this.bno = bno;
}
public int getGrp() {
return grp;
}
public void setGrp(int grp) {
this.grp = grp;
}
public int getGrps() {
return grps;
}
public void setGrps(int grps) {
this.grps = grps;
}
public int getGrpl() {
return grpl;
}
public void setGrpl(int grpl) {
this.grpl = grpl;
}
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 String getWdate() {
return wdate;
}
public void setWdate(String wdate) {
this.wdate = wdate;
}
}
3-5. SQL(mapper)
> mapper.xml
<!-- 댓글 쓰기 -->
<insert id="picture_reply_write" parameterType="com.exam.model1.pictureReply.ReplyTO">
insert into p_reply
values(0, #{bno}, #{grp}, 0, 0, #{writer}, #{content}, now())
</insert>
<!-- 모댓글일경우 no, grp 일치하게 함 -->
<update id="picture_reply_check" parameterType="com.exam.model1.pictureReply.ReplyTO">
update p_reply set grp=#{grp}
where no != grp
</update>
<!-- 모댓글이 삭제된 댓글일때 그에 딸린 답글들이 모두삭제되면 테이블에서 완전히 삭제한다 -->
<delete id="picture_reply_delete_after_rereply_delete" parameterType="com.exam.model1.pictureReply.ReplyTO">
delete from p_reply
where content="" and grp=#{grp}
</delete>
<!-- 답글 쓰기 -->
<insert id="picture_rereply_write" parameterType="com.exam.model1.pictureReply.ReplyTO">
insert into p_reply
values(0, #{bno}, #{grp}, 0, #{grpl}, #{writer}, #{content}, now())
</insert>
<!-- p_board에 댓글수 증가 -->
<update id="picture_reply_up" parameterType="com.exam.model1.picture.PictureTO">
update p_board set reply=reply+1
where no=#{no}
</update>
<!-- 댓글 리스트 가져오기 -->
<select id="picutre_replyList" parameterType="com.exam.model1.pictureReply.ReplyTO" resultType="com.exam.model1.pictureReply.ReplyTO">
select r.no, r.bno, r.grp, r.grpl, r.writer, r.content, date_format(wdate,'%Y-%m-%d') wdate, datediff(now(), wdate) wgap , u.profile
from p_reply r left outer join user u
on r.writer = u.nick
where r.bno = #{bno}
order by grp asc, grps desc
</select>
<!-- 댓글 추가/삭제시 댓글 갯수 가져오기 -->
<select id="picture_reply_count" parameterType="com.exam.model1.picture.PictureTO" resultType="com.exam.model1.picture.PictureTO">
select reply
from p_board
where no=#{no}
</select>
<!-- 모댓글의 답글수를 카운트 -->
<select id="picture_count_rereply" parameterType="com.exam.model1.pictureReply.ReplyTO" resultType="int">
select count(no)
from p_reply
where no != #{no} and grp = #{no}
</select>
<!-- 답글수를 카운트 -->
<select id="picture_count_rereply_fromrereply" parameterType="com.exam.model1.pictureReply.ReplyTO" resultType="int">
select count(no)
from p_reply
where no != #{grp} and grp = #{grp}
</select>
<!-- 모댓글 삭제 - 답글 없음 -->
<delete id="picture_reply_delete" parameterType="com.exam.model1.pictureReply.ReplyTO">
delete from p_reply
where no=#{no}
</delete>
<!-- 모댓글 삭제 - 답글 있음 -->
<update id="picture_reply_not_delete" parameterType="com.exam.model1.pictureReply.ReplyTO">
update p_reply set content=""
where no=#{no}
</update>
<!-- p_board에 댓글수 감소 -->
<update id="picture_reply_down" parameterType="com.exam.model1.picture.PictureTO">
update p_board set reply=reply-1
where no=#{no}
</update>
'빅데이터 플랫폼 구축을 위한 자바 개발자 양성과정 > 랜선여행 커뮤니티 프로젝트' 카테고리의 다른 글
Spring MVC 로 메세지 기능 구현 (42) | 2021.04.27 |
---|---|
Spring MVC 로 무한 스크롤, 검색 구현하기 (8) | 2021.04.22 |
Spring MVC 로 하트(좋아요) 구현하기 - 모달창 포함 (10) | 2021.04.21 |
Web socket을 활용한 실시간 댓글 알람 기능 구현 (0) | 2021.03.30 |
페이지에서 '좋아요' 를 하고 뒤로 가기 한 후 다시 돌아왔을때 처리 (0) | 2021.03.29 |