[42일차] 댓글 등록, 삭제, 수정 기능 구현
<실습>
1. Reply List 버튼 토글 기능 구현
2. 댓글 등록 기능 구현
3. 댓글 삭제 기능 구현
4. 댓글 수정 기능 구현
댓글 관련 기능은 board_view.jsp에서 수정/추가한다.
Reply List 버튼을 누르면 댓글이 나타나고, 다시 한 번 누르면 댓글이 사라지는 토글 기능을 구현한다.
- Reply List 버튼영역의 span태그에 data-toggle="collapse"와 data-target="#div_reply" 를 추가한다.
*collapse : 병합
<div class="time-label" >
<span data-toggle="collapse" data-target="#div_reply" class="bg-red" id="btn_reply_list" style="cursor:pointer;">Reply List[<span id="reply_count">${boardVO.reply_count}</span>]
</span>
</div>
- time-label 클래스 영역 바로 아래에 버튼 클릭 시, 병합될 (나타나거나 사라질) 영역, 타겟인 #div_reply을 설정한다.
#div-reply 영역 안에는 댓글 목록부터 댓글 페이지 영역까지 포함한다.
<div id="div_reply" class="timeline collapse">
<!-- append(내부) 토글영역 -->
<!-- 페이징처리 시작 -->
<div class="pagination justify-content-center">
<ul class="pagination pageVO">
<!--
<li class="paginate_button page-item previous disabled" id="example1_previous"><a href="#" aria-controls="example1" data-dt-idx="0" tabindex="0" class="page-link">«</a></li>
previous (위)
<li class="paginate_button page-item active"><a href="#" aria-controls="example1" data-dt-idx="1" tabindex="0" class="page-link">1</a></li>
<li class="paginate_button page-item "><a href="#" aria-controls="example1" data-dt-idx="2" tabindex="0" class="page-link">2</a></li>
next (아래)
<li class="paginate_button page-item next" id="example1_next"><a href="#" aria-controls="example1" data-dt-idx="7" tabindex="0" class="page-link">»</a></li>
-->
</ul>
</div>
<!-- 페이징처리 끝 -->
</div>
Ajax로 Reply List버튼 클릭 시, REST API 컨트롤러를 호출해서 댓글 목록 나타내도록 한다.
- printReplyList 함수로 토글기능 대상인 #div_reply 영역에 댓글리스트를 출력한다.
* prepend : target의 기존 내용 앞에 추가한다.
<!-- 화면을 재구현Representation하는 함수(아래) -->
<script>
var printReplyList = function(data, target, templateObject) {
var template = Handlebars.compile(templateObject.html()); //html태그로 변환
var html = template(data); //빅데이터를 리스트탬플릿에 바인딩(데이터결합,묶음) 역할. 변수 html에 저장되었음.
$(".template-div").remove(); // 화면에 보이는 댓글리스트만 지우기
//target.after(html); //target은 .time-label 클래스 영역을 가리킨다.
//target.append(html); //target은 #div_reply 아이디 영역을 가리킨다. append는 내부내용 기존 내용의 뒤에 추가
target.prepend(html); //prepend 내부 내용 추가시 기존 내용의 앞에 추가한다.
};
</script>
- 우리는 스크립트 맨 위에, 댓글 페이지 값을 1로 지정했다. 이것은 Reply List 버튼을 눌렀을 때 처음 페이지 값이다.
input type="hidden" id="reply_page" value="1"><!-- #btn_reply_list 클릭할 때 가져올 페이지 값 -->
- 현재 페이지 값을 page변수에 저장한다.
- ajax로 REST API 컨트롤러를 호출한다.
- POST 형식으로 게시물 번호가 ${boardVO.bno}이고 댓글 페이지 번호가 page인 reply_list의 댓글 목록을 가져올 것이다. 가져온 값은 JSON데이터로 반환한다.
- 댓글 목록 가져오기를 success 했을 때, ① 댓글이 존재하지 않는다면 화면 내용을 클리어하고(DB와는 관련 X) 조회된 값이 없음을 alert로 알린다. ② 댓글이 존재한다면, #div_reply 영역에 가져온 댓글 리스트를 출력한다.
- 댓글 페이지 데이터는 pageVO클래스에 파싱한다.
<!-- btn_reply_list버튼에 적용한 ajax로 댓글 리스트를 구하는 함수를 외부로 뺀다. -->
<!-- 왜? btn_reply_list에 토글기능을 적용돼서, 토글기능과 Ajax기능을 분리하는 목적 -->
<script>
var replyList = function() {
var page = $("#reply_page").val();
$.ajax({
type:"post",
url:"/reply/reply_list/${boardVO.bno}/"+page, //게시물번호에 대한 댓글 목록을 가져오는 URL
dataType:"json", //받을 때 JSON데이터를 받는다.
success:function(result){ //result에는 댓글 목록을 Json데이터로 받는다.
if(typeof result=="undefined" || result == null || result == ""){
$("#div_reply").empty(); //조회된 값 없을 때, 화면 내용 클리어.
alert("조회된 값이 없습니다.");
}else{
//위에서 정의한 printReplyList(Json데이터, 출력위치타켓, 빵틀);
printReplyList(result.replyList, $("#div_reply"), $("#template"));//화면에 출력하는 구현함수를 호출하면 실행.
// result.pageVO 데이터를 .pageVO클래스 영역에 파싱한다.
printPageVO(result.pageVO, $(".pageVO")); //pageVO는 ReplyController에서 가져온것
}
},
error:function(result){
alert("RestAPI서버에 문제가 발생했습니다. 다음에 이용해주세요.");
}
});
}
</script>
<!-- 댓글 리스트 버튼 클릭 시, RestAPI컨트롤러 호출(아래)해서 댓글목록 Json데이터로 -->
<script>
$(document).ready(function(){
$("#btn_reply_list").on("click", function(){ //부트스트랩의 토글기능이 자동적용
replyList(); // 댓글리스트를 ajax로 호출하는 함수 실행
});
});
</script>
앞서 댓글 페이징 처리를 했을 때, 댓글 페이지 번호를 눌렀을 때, Reply List 버튼을 누르는 것과 같은 기능을 하도록 했다. 하지만 Reply List버튼에 토글기능을 부여하면서, 토글기능과 댓글 페이지 번호 이동 기능을 분리했다.
- 따라서, 클릭한 페이지번호(링크)의 값을 page변수에 저장하여 #reply_page에 반환한다. 그 반환한 값을 가진 replyList() 함수를 호출하여 리스트를 불러온다.
<script>
$(document).ready(function(){
$(".pageVO").on("click", "li a", function(event){ // 댓글 페이지 링크 영역을 클릭했을 때 이벤트
event.preventDefault(); // a태그의 기본기능인 이동기능을 막아준다.
var page = $(this).attr("href"); //현재 클릭한 페이지 값을 저장.
$("#reply_page").val(page);
// $("#btn_reply_list").click(); //페이지번호에서 현재 페이지 번호를 클릭했을 떄, btn_reply_list버튼을 누르는것과 같은 역할
//위 버튼을 클릭하면 토글기능이 작동되기 때문에 댓글 목록만 가져오는 replyList함수 실행 (아래)
replyList();
});
});
댓글 등록 버튼을 눌렀을 때, 실제로 댓글이 DB에 저장되고, 댓글 리스트에 추가되도록 구현한다.
기능을 구현하기 전에, replytext -> reply_text로 수정한다.
<div class="form-group">
<label for="reply_text">Reply Text</label>
<input type="text" class="form-control" name="reply_text" id="reply_text" placeholder="내용 입력해주세요." required>
</div>
Reply List [ ${boardVO.reply_count} ] 를 Reply List [<span id="reply_count">${boardVO.reply_count}</span>] 로 수정한다.
댓글등록버튼을 클릭했을 때 구현 내용을 더미데이터를 없애고 실제 데이터가 입력/저장되도록 수정한다.
- 댓글등록버튼을 클릭하면 함수를 실행한다.
- bno에 ${boardVO.bno}, reply_text에 #reply_text, replyer에 #replyer를 저장한다.
(만약 reply_text 나 replyer가 빈값이라면 alert경고 메세지를 띄우고 더이상 코드를 실행하지 않는다.)
- POST로 reply_write에 전송된 데이터를 text형식으로 받는다.
- 데이터를 백엔드로 보낼 때는 JSON형식으로 보낼것이다. headers, data 설정
- DB 필드명 bno, repy_text, replyer 에 각각 변수 bno, reply_text, replyer에 저장된 값을 보내준다.
- 댓글 내용 전송에 success하면 #reply_count의 값을 불러와 reply_count에 반환한다. 그 변수에 +1한 값을 다시 #reply_count에 저장한다.
var reply_count = $(#reply_count).text();
$(#reply_count).text(parseInt(reply_count)+1);
- 등록 후, 댓글 페이지는 1페이지로 이동하고 댓글 리스트를 불러온다.
- 댓글 등록 란의 내용들은 빈칸으로 초기화한다. ${#replyer}.val() , ${#reply_text}.val()
- 댓글 내용 전송에 실패하면 경고 메세지를 alert로 띄운다.
<script>
$(document).ready(function(){
$("#insertReplyBtn").on("click", function() { //댓글등록버튼 클릭했을 때 구현 내용
// Ajax 이용해서, 화면을 Representation (REST-API방식) 부분 화면을 재구현
// REST API 서버 단에 보낼 변수 값 정의(아래)
var bno = "${boardVO.bno}"; //자바변수
var reply_text=$("#reply_text").val(); //jquery변수, input태그값
var replyer=$("#replyer").val(); //jquery변수, input태그값
if(reply_text=="" || replyer ==""){
alert("댓글내용과 댓글 작성자 입력은 필수입니다");
return false;
}
$.ajax({
type:'post', //지금은 html이라서 get방식이지만, jsp로 가면 post방식으로 바꿔야한다.(보안)
url:'/reply/reply_write', //jsp로 가면, ReplyController에서 지정한 url로 변경
dataType:'text', //ReplyController로부터 데이터를 text형식으로 받겠다고 명시.
// 백엔드로 보내주는 작업 (아래)
headers:{ //데이터를 json으로 보낼 것이다.
"Content-Type":"application/json",
"X-HTTP-Method-Override":"POST"
},
data:JSON.stringify({
// Key:Value
bno:bno,
reply_text:reply_text,
replyer:replyer
}),
success:function(result){ //응답이 성공하면(상태값 200 OK), 위 경로에서 반환받은 result(JSON 텍스트 데이터) 이용해서 화면 재구현
//printReplyList(빅데이터, 출력할 타겟 위치, 빅데이터를 가지고 바인딩된-묶인 템플릿화면))
//printReplyList(result, $(".time-label"), $("#template"));//화면에 출력하는 구현함수를 호출하면 실행.
//result값을 받아서 #template으로 가공시켜서 time-label 위치에 출력한다.
//입력이 success된 후에 댓긁수+1, 페이지는 1페이지로 이동, replyList() 댓글목록 호출
var reply_count = $("#reply_count").text(); //$("영역").text(영역안쪽의문자열), $("영역").val(input데이터)
//alert(reply_count);디버그
$("#reply_count").text(parseInt(reply_count)+1);
$("#reply_page").val("1");
replyList();
$("#replyer").val(""); //등록후 빈칸으로
$("#reply_text").val(""); //등록후 빈칸으로
},
error:function(result){
alert("RestAPI서버가 작동하지 않습니다.");
}
});
});
});
</script>
댓글 수정 버튼을 눌렀을 때, 모달창이 뜬다. 그 모달창에는 닫기, 수정, 삭제 버튼이 있다.
모달창의 삭제버튼을 눌렀을 때 해당 댓글이 삭제되도록 구현한다.
기능 구현하기 전에, 아이디를 변경해준다. replytext -> reply_text_modal
- 삭제 버튼을 눌렀을 때 함수를 실행한다.
- 변수 rno에 클릭한 댓글 번호 #rno를 저장한다.
- DELETE형으로 reply_delete의 게시물 번호 ${boardVO.bno}의 댓글번호 rno에 삭제 요청이 들어오면 text형식으로 받는다.
- 삭제 요청이 success하면 모달창을 숨기고 삭제 완료 메시지를 alert로 띄운다.
- 변수 reply_count에 현재 #reply_count의 값을 저장하고, #reply_count에 변수 reply_count에 -1한 값을 반환한다.
- 댓글리스트를 호출하여 화면에 출력한다.
- 삭제 요청이 실패하면 경고 메시지를 alert로 띄운다.
<!-- 댓글 삭제 버튼 액션 처리 (아래) -->
<script>
$(document).ready(function(){
$("#deleteReplyBtn").on("click", function(){
var rno = $("#rno").val(); //삭제할 댓글 번호값 변수
//alert("선택한 댓글 번호: "+rno);
$.ajax({
type:"delete",
url:"/reply/reply_delete/${boardVO.bno}/"+rno,
dataType:"text", //반환값 문자열
success:function(result){
if(result=="success"){
$("#replyModal").modal("hide");
alert("삭제가 완료되었습니다.");
var reply_count = $("#reply_count").text();//$("영역").val(input데이터),
$("#reply_count").text(parseInt(reply_count)-1);//$("영역").text(영역안쪽의문자열)
replyList(); //댓글리스트 메소드 호출
}
},
error:function(result){
alert("RestAPI서버가 작동하지 않습니다."); //모달창(팝업창) 숨기기
}
});
});
});
</script>
위의 내용은 댓글 한 개를 선택해서 삭제하는 것을 구현했다.
게시물 자체를 삭제할 때 그 안에 있는 댓글도 함께 삭제되도록 구현한다.
Mapper, DAO에 deleteRelyAll 을 추가한다.
<delete id="deleteReplyAll">
DELETE FROM tbl_reply WHERE bno=#{bno}
</delete>
public void deleteReplyAll(Integer bno) throws Exception;
@Override
public void deleteReplyAll(Integer bno) throws Exception {
// 매퍼쿼리 연결
sqlSession.delete("replyMapper.deleteReplyAll", bno);
}
그리고 BoardServiceImpl의 deleteBoard 구현부에도 소스를 추가한다.
replyDAO.deleteReplyAll(bno);
댓글 수정버튼을 눌렀을 때, 모달창에서 댓글 내용을 수정하고 모달창의 수정버튼을 눌렀을 때 DB에 반영되고 화면에 출력되는 기능을 구현한다.
<!-- 댓글 수정 버튼 액션 처리(아래) -->
<script>
$(document).ready(function(){
$("#updateReplyBtn").on("click", function(){
var rno = $("#rno").val(); //모달창의 input태그 값 변수
var reply_text_modal = $("#reply_text_modal").val();//모달창의 input태그값 변수
$.ajax({
type:"patch",
url:"/reply/reply_update",
headers:{
"Content-Type":"application/json",
"X-HTTP-Method-Override":"PATCH"
},
data:JSON.stringify({ //JSON데이터로 변환해서 REST API서버로 전송
rno:rno,
reply_text:reply_text_modal
}),
dataType:"text", // REST API에서 text형식으로 반환
success:function(result){
if(result=="success"){
$("#replyModal").modal("hide");
alert("수정이 완료되었습니다.");
replyList();
}
},
error:function(result){
alert("RestAPI서버가 작동하지 않습니다.");
}
});
});
});
</script>
스크립트는 정의기능과 호출 기능의 위치 선정이 중요하다. 정의가 먼저, 호출이 나중에.