4/17/2018

(구버전) 스프링 게시판 만들기 #10. 댓글 수정/삭제 기능 구현

'(구버전) 스프링 게시판 만들기'는 내용이 부족하다고 판단하여
스프링 게시판 만들기를 새로 작성하였습니다.

링크 및 참조용으로 현재 게시물은 남겨두겠지만,
가급적이면 새로운 스프링 게시판 만들기를 참조해주시기 바랍니다.

댓글 작성 기능을 구현했으니, 작성한 댓글을 수정하거나 삭제하는 기능을 구현하겠습니다.

게시판 수정/삭제와 유사하게, 댓글 수정/삭제 버튼을 클릭하면 별도의 페이지로 이동하여 수정/삭제하는 방식으로 하겠습니다.

먼저, 댓글 수정/삭제 쿼리를 실행해봅니다.

댓글을 수정할 때 필요한 사항은 수정된 댓글의 내용(content)과 댓글 번호(rno)입니다. 삭제할 땐 댓글 번호(rno)만 있으면 됩니다.

수정/삭제는 이렇게만 있으면 되는데, 수정/삭제를 하기전에 별도 페이지로 이동하여 작업하는 구조이니, 선택한 댓글 1개만 가져올 쿼리가 추가적으로 필요합니다.

댓글 1개만 가져오는 쿼리, 댓글을 수정하는 쿼리, 댓글을 삭제하는 쿼리, 이렇게 3개를 매퍼에 추가합니다.

<!-- 특정 댓글 조회 -->
<select id="readReplySelect" resultType="com.kuzuro.domain.ReplyVO">
 select
     bno, rno, content, writer, regDate
 from myReply
     where rno = #{rno}
</select>

<!-- 댓글 수정 -->
<update id="updateReply">
 update myReply
     set
         content = #{content}
     where rno = #{rno}
</update>

<!-- 댓글 삭제 -->
<delete id="deleteReply">
 delete from myReply
  where rno = #{rno}
</delete>

ReplyDAO와 ReplyService에 코드를 추가합니다.

댓글 삭제는 댓글 번호(rno)만 사용하기 때문에, ReplyVO 대신 int rno를 사용해도 됩니다.

컨트롤러에 댓글 수정/삭제하는 POST 메서드를 작성합니다. 이 메서드는 게시물 수정/삭제하는 POST 메서드와 유사합니다.

// 댓글 수정 POST
@RequestMapping(value = "/replyUpdate", method = RequestMethod.POST)
public String replyUpdate(ReplyVO vo, SearchCriteria scri, RedirectAttributes rttr) throws Exception {
 logger.info("reply update");
 
 RepService.replyUpdate(vo);
 
 rttr.addAttribute("bno", vo.getBno());
 rttr.addAttribute("page", scri.getPage());
 rttr.addAttribute("perPageNum", scri.getPerPageNum());
 rttr.addAttribute("searchType", scri.getSearchType());
 rttr.addAttribute("keyword", scri.getKeyword());
 
 return "redirect:/board/read";
}

// 댓글 삭제 POST
@RequestMapping(value = "/replyDelete", method = RequestMethod.POST)
public String replyDelete(ReplyVO vo, SearchCriteria scri, RedirectAttributes rttr) throws Exception {
 logger.info("reply delete");
 
 RepService.replyDelete(vo);
 
 rttr.addAttribute("bno", vo.getBno());
 rttr.addAttribute("page", scri.getPage());
 rttr.addAttribute("perPageNum", scri.getPerPageNum());
 rttr.addAttribute("searchType", scri.getSearchType());
 rttr.addAttribute("keyword", scri.getKeyword());
 
 return "redirect:/board/read";
}

이 메서드들은 POST이므로, 페이지에서 컨트롤러로 전송된 값이 Service와 DAO, 매퍼를 통하여 작업하게 됩니다.

저는 별도의 페이지로 이동한 뒤 수정/삭제 작업을 할거라 별도의 jsp파일을 생성하고, 이 jsp를 연결해주는 GET 메서드를 작성해야합니다.

댓글 내용 밑부분에 수정 페이지와 삭제 페이지로 이동할 수 있는 버튼과 스크립트를 추가합니다.

<p>              
 <button type="button" class="replyUpdate" data-rno="${repList.rno}">수정</button>
 <button type="button" class="replyDelete" data-rno="${repList.rno}">삭제</button>
 
 <script>
  $(".replyUpdate").click(function(){
   self.location = "/board/replyUpdate?bno=${read.bno}"
    + "&page=${scri.page}"
    + "&perPageNum=${scri.perPageNum}"
    + "&searchType=${scri.searchType}"
    + "&keyword=${scri.keyword}"
    + "&rno=" + $(this).attr("data-rno");        
  });
  
  $(".replyDelete").click(function(){
   self.location = "/board/replyDelete?bno=${read.bno}"
    + "&page=${scri.page}"
    + "&perPageNum=${scri.perPageNum}"
    + "&searchType=${scri.searchType}"
    + "&keyword=${scri.keyword}"
    + "&rno=" + $(this).attr("data-rno"); 
  });       
 </script>
</p>

게시물에 보여지는 댓글은 다수의 댓글이 한번에 표시되기 때문에, 버튼만 추가하면 어느 댓글에 대한 수정/삭제 버튼을 눌렀는지 바로 알아내기가 어렵습니다.

그렇기 때문에 버튼에 data속성을 이용하여 댓글 번호(rno)를 부여했고, 스크립트에서 $(this).attr("data-rno")를 사용하여 가져올 수 있습니다. 여기서 $(this) 는 클릭한 요소를 가르킵니다.

컨트롤러에 댓글 수정 페이지와 댓글 삭제 페이지를 위한 GET 메서드를 추가합니다.

// 댓글 수정 GET
@RequestMapping(value = "/replyUpdate", method = RequestMethod.GET)
public void getReplyUpdate(@RequestParam("rno") int rno,
      @ModelAttribute("scri") SearchCriteria scri, Model model) throws Exception {
 logger.info("reply update");
 
 ReplyVO vo = null;
 
 vo = RepService.readReplySelect(rno);
 
 model.addAttribute("readReply", vo);
 model.addAttribute("scri", scri);
}

// 댓글 수정 GET
@RequestMapping(value = "/replyDelete", method = RequestMethod.GET)
public void getReplyDelete(@RequestParam("rno") int rno,
      @ModelAttribute("scri") SearchCriteria scri, Model model) throws Exception {
 logger.info("reply delete");
 
 ReplyVO vo = null;
 
 vo = RepService.readReplySelect(rno);
 
 model.addAttribute("readReply", vo);
 model.addAttribute("scri", scri);
}

이 메서드는 댓글 번호(rno)를 이용하여 선택한 댓글 하나만 가져오며, SearchCriteria를 이용하여 이전 페이지의 정보를 가져오며 이 정보로 수정/삭제가 완료되거나 취소되었을 때 원래의 게시물로 이동할 수 있도록 합니다.

modify.jsp파일을 복사/붙여넣기하여 replyUpdate.jsp로 이름을 변경한 뒤 코드를 수정합니다.

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<html>
<head>
 <title>kuzuro 게시판</title>
 
 <!-- 제이쿼리 -->
 <script src='https://code.jquery.com/jquery-3.3.1.min.js'></script>
 
</head>
<body>

<div id="root">
 <header>
  <%@include file="include/header.jsp" %>
 </header>
 <nav>
  <%@include file="include/nav.jsp" %>
 </nav>
 <section id="container">
  <form role="form" method="post" autocomplete="off">
    
   <input type="hidden" id="bno" name="bno" value="${readReply.bno}" readonly="readonly" />
   <input type="hidden" id="rno" name="rno" value="${readReply.rno}" readonly="readonly" /> 
   <input type="hidden" id="page" name="page" value="${scri.page}" readonly="readonly" />
   <input type="hidden" id="perPageNum" name="perPageNum" value="${scri.perPageNum}" readonly="readonly" />
   <input type="hidden" id="searchType" name="searchType" value="${scri.searchType}" readonly="readonly" />
   <input type="hidden" id="keyword" name="keyword" value="${scri.keyword}" readonly="readonly" />
   
   <p>
    <label for="content">글 내용</label>
    <textarea id="content" name="content" >${readReply.content}</textarea>
   </p>
   
   <p>
    <button type="submit">수정</button>
    <button type="button" id="cancel_btn">취소</button>
    
    <script>
    // 폼을 변수에 저장
    var formObj = $("form[role='form']"); 
    
    // 취소 버튼 클릭
    $("#cancel_btn").click(function(){   
     self.location = "/board/read?bno=${readReply.bno}"
     + "&page=${scri.page}"
     + "&perPageNum=${scri.perPageNum}"
     + "&searchType=${scri.searchType}"
     + "&keyword=${scri.keyword}";
    });
    </script>
   </p> 
  </form>
 </section>
 <footer>
  <%@include file="include/footer.jsp" %>  
 </footer>
</div>
</body>
</html>

replyUpdate.jsp 파일을 복사/붙여넣기하여 replyDelete.jsp로 이름을 변경한 뒤 코드를 수정합니다.

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<html>
<head>
 <title>kuzuro 게시판</title>
 <!-- 제이쿼리 -->
 <script src='https://code.jquery.com/jquery-3.3.1.min.js'></script>
</head>
<body>
<div id="root">
 <header>
  <%@include file="include/header.jsp" %>
 </header>
 <nav>
  <%@include file="include/nav.jsp" %>
 </nav>
 <section id="container">
  <form role="form" method="post" autocomplete="off">
   <input type="hidden" id="bno" name="bno" value="${readReply.bno}" readonly="readonly" />
   <input type="hidden" id="rno" name="rno" value="${readReply.rno}" readonly="readonly" />
   <input type="hidden" id="page" name="page" value="${scri.page}" readonly="readonly" />
   <input type="hidden" id="perPageNum" name="perPageNum" value="${scri.perPageNum}" readonly="readonly" />
   <input type="hidden" id="searchType" name="searchType" value="${scri.searchType}" readonly="readonly" />
   <input type="hidden" id="keyword" name="keyword" value="${scri.keyword}" readonly="readonly" />
   
   <p>정말로 삭제하시겠습니까?</p>
   <p>
    <button type="submit">예, 삭제합니다.</button><br />
    <button type="button" id="cancel_btn">아니오, 삭제하지 않습니다.</button>

    <script>
    // 폼을 변수에 저장
    var formObj = $("form[role='form']"); 
    
    // 취소 버튼 클릭
    $("#cancel_btn").click(function(){
     
     self.location = "/board/read?bno=${readReply.bno}"
      + "&page=${scri.page}"
      + "&perPageNum=${scri.perPageNum}"
      + "&searchType=${scri.searchType}"
      + "&keyword=${scri.keyword}";      
    });
    </script>
   </p>
  </form>
 </section>
 <footer>
  <%@include file="include/footer.jsp" %> 
 </footer>
</div>
</body>
</html>

프로젝트를 실행한 후 게시물에 댓글을 새로 작성합니다.

댓글에 있는 수정 버튼을 클릭합니다.

replyUpdate로 이동되었으며, 댓글의 내용을 수정합니다.

등록되어있던 댓글의 내용이 수정되었습니다.

삭제 버튼을 클릭하면 replyDelete로 이동되어 삭제 여부를 한번 확인하고, 삭제 버튼을 클릭하면

등록되어있는 댓글이 삭제됩니다.

게시물 수정
  1. 안녕하세요
    data-rno 와
    $(this)의 설명 한번만 부탁드립니다

    답글삭제
    답글
    1. 안녕하세요?

      datra-rno는 HTML 커스텀 속성입니다. 예를들어 버튼 태그가 있다고 합시다. <button type="button" id="btn_1" style="width:200px;">버튼</button> 이 버튼에는 type, id, style이라는 속성이 있습니다. 여기에 개발자가 자신만의 속성을 추가할 수 있는데, 그게 커스텀 속성입니다.

      커스텀 속성은 기본적으로 data-[키워드]로 만들지만, 이렇게 하지 않는다고해서 작동하지 않는건 아닙니다. 다만 관리 및 구분하기에 용이하므로 가급적이면 따르는게 좋습니다.

      본문에 있는건 rno의 값을 담기 위해 추가한 속성이라 data-rno라고 한겁니다.

      -

      this는 다른 언어에서도 사용되는 명칭인데, 간단히 설명하면 현재 실행중이거나 선택된 요소를 의미합니다.


      본문에 있는 코드로 설명하자면

      $(".replyUpdate").click(function(){
      self.location = "/board/replyUpdate?bno=${read.bno}"
      + "&page=${scri.page}"
      + "&perPageNum=${scri.perPageNum}"
      + "&searchType=${scri.searchType}"
      + "&keyword=${scri.keyword}"
      + "&rno=" + $(this).attr("data-rno");
      });


      여기서 $(this)$(".replyUpdate")를 의미합니다.

      그러므로 는 $(".replyUpdate").attr("data-rno");와 동일한 코드입니다.

      삭제