10/18/2018

스프링 쇼핑몰 만들기 #20. 상품 소감(댓글) 수정 구현

스프링 쇼핑몰 만들기
- 깃허브 링크

소감(댓글) 수정도 소감 삭제처럼 에이젝스(Ajax)를 이용해서 하겠습니다.

view.jsp에서 </body> 바로 위에 코드를 추가합니다.

<div class="replyModal">

<div class="modalContent">

<div>
<textarea class="modal_repCon" name="modal_repCon"></textarea>
</div>

<div>
<button type="button" class="modal_modify_btn">수정</button>
<button type="button" class="modal_cancel">취소</button>
</div>

</div>

<div class="modalBackground"></div>

</div>

이 코드는 모달 창(modal window)으로 사용됩니다.

스타일도 추가합니다.

<style>
div.replyModal { position:relative; z-index:1; }
div.modalBackground { position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0, 0, 0, 0.8); z-index:-1; }
div.modalContent { position:fixed; top:20%; left:calc(50% - 250px); width:500px; height:250px; padding:20px 10px; background:#fff; border:2px solid #666; }
div.modalContent textarea { font-size:16px; font-family:'맑은 고딕', verdana; padding:10px; width:500px; height:200px; }
div.modalContent button { font-size:20px; padding:5px 10px; margin:10px 0; background:#fff; border:1px solid #ccc; }
div.modalContent button.modal_cancel { margin-left:20px; }
</style>

스타일까지 모두 입력하면, 검은색의 반투명한 배경에 흰색 입력창이 있는걸 확인할 수 있습니다.

당연하지만 수정 버튼이나 취소 버튼을 클릭해봐도 아무 동작은 없습니다.

이제 div.replyModal 의 스타일 마지막에 display:none; 를 추가해 화면에서 숨깁니다.

모달이 안보이게 되었습니다.

상품 소감(댓글) 수정에서 추가했던 스크립트에 수정 버튼 스크립트를 추가합니다.

$(document).on("click", ".modify", function(){
$(".replyModal").attr("style", "display:block;");
});

이번엔 </body> 바로 위에 스크립트를 추가합니다.

<script>
$(".modal_cancel").click(function(){
$(".replyModal").attr("style", "display:none;");
});
</script>

여기까지 했다면, 상품 소감 목록에서 M 버튼(수정 버튼)을 클릭하면 모달이 나오고, 취소 버튼을 누르면 모달이 안보이게 됩니다.

단순히 스타일을 이용해 보이고/숨기는것보단, 이왕 제이쿼리를 사용하고 있으니 제이쿼리에 내장된 페이드(fade) 기능을 이용하는게 더 보기 좋을것 같습니다.

.fadeIn() 은 서서히 나타나고, .fadeOut() 은 서서히 사라집니다. 괄호 안의 숫자는 시간이며 1000 = 1초로 되어있는데, 200이라면 0.2초가 됩니다.

그리고 기능적인 부분은 아니지만, 목록을 출력하는 코드를 살짝 수정합니다. 원래는 상품 소감을 구분할 번호를 넣어야했는데, 실수로 상품 번호를 넣어버렸네요.

여기에 들어가는 상품 번호로 작동되는 기능은 없으니 꼭 할 필요는 없습니다.

소감 목록의 M 버튼(수정 버튼)에 코드를 추가합니다.

$(document).on("click", ".modify", function(){
//$(".replyModal").attr("style", "display:block;");
$(".replyModal").fadeIn(200);

var repNum = $(this).attr("data-repNum");
var repCon = $(this).parent().parent().children(".replyContent").text();

$(".modal_repCon").val(repCon);
$(".modal_modify_btn").attr("data-repNum", repNum);

});

변수 repNum에는 버튼에 부여된 소감 번호(data-repNum)를 저장, 변수 repCon에는 버튼의 부모(parent)의 부모에서 자식의 클래스가 replyContent인 요소의 값을 저장했습니다.

M 버튼의 부모는 <div class="replyFooter">, <div class="replyFooter"> 의 부모는 <li data-repNum="11">, <li data-repNum="11"> 의 자식중 클래스가 replyContent인건 '소감 내용'입니다.

repNum에 저장된 소감 번호는 모달창의 버튼에 부여하고, repCon에 저장된 소감 내용은 모달창의 텍스트에어리어에 부여합니다.

이제 M버튼을 눌러서 나오는 모달창의 텍스트에어리어에는 선택했던 버튼에 해당되는 소감 내용이 들어가있고, 수정 버튼에는 선택했던 버튼의 상품 번호가 부여되어있습니다.

이제 모달창의 수정버튼을 클릭하면 컨트롤러로 데이터가 전달될 수 있도록 스크립트를 추가합니다.

$(".modal_modify_btn").click(function(){
var modifyConfirm = confirm("정말로 수정하시겠습니까?");

if(modifyConfirm) {
var data = {
repNum : $(this).attr("data-repNum"),
repCon : $(".modal_repCon").val()
};  // ReplyVO 형태로 데이터 생성

$.ajax({
url : "/shop/view/modifyReply",
type : "post",
data : data,
success : function(result){

if(result == 1) {
replyList();
$(".replyModal").fadeOut(200);
} else {
alert("작성자 본인만 할 수 있습니다.");
}
},
error : function(){
alert("로그인하셔야합니다.")
}
});
}

});

상품 소감 삭제에서 했던것과 마찬가지로 컨펌(confirm)을 이용해 수정 여부를 확인하고, 데이터를 제이슨(Json) 형태로 저장한 뒤 에이젝스(Ajax)를 이용해 컨트롤러로 값을 전달합니다. 컨트롤러에서 되돌아오는 result의 값이 1이면 성공, 0이면 실패한것으로 구분하여 작업합니다.

먼저 쿼리를 작성합니다.

소감 삭제와 마찬가지로 사용자의 아이디를 구분해야합니다.

작성한 쿼리를 매퍼에 추가합니다.

<!-- 상품 소감(댓글) 수정 -->
<update id="modifyReply">
update tbl_reply
    set
        repCon = #{repCon}
    where repNum = #{repNum}
        and userId = #{userId}
</update>

DAO와 Service를 작성합니다.

컨트롤러에 소감 수정용 메서드를 추가합니다.

// 상품 소감(댓글) 수정
@ResponseBody
@RequestMapping(value = "/view/modifyReply", method = RequestMethod.POST)
public int modifyReply(ReplyVO reply, HttpSession session) throws Exception {
logger.info("modify reply");

int result = 0;

MemberVO member = (MemberVO)session.getAttribute("member");
String userId = service.idCheck(reply.getRepNum());

if(member.getUserId().equals(userId)) {

reply.setUserId(member.getUserId());
service.modifyReply(reply);
result = 1;
}

return result;
}

메서드의 형태는 소감 삭제 메서드와 크게 다르지 않습니다.

소감을 작성한 사용자의 아이디와 현재 로그인한 사용자의 아이디를 비교한 뒤, 둘이 같다면 수정 작업을 하고 result에 1을 저장하고, 같지 않다면 그대로 종료합니다.

내용을 수정하고, 수정 버튼을 클릭하면 확인 메시지가 나옵니다. 메시지의 확인을 선택하면 수정 작업이 진행됩니다.

내용이 수정된걸 확인할 수 있습니다.

마지막으로 목록을 출력하는 스크립트에 코드를 추가합니다.

str += "<li data-repNum='" + this.repNum + "'>" //"<li data-gdsNum='" + this.gdsNum + "'>"
+ "<div class='userInfo'>"
+ "<span class='userName'>" + this.userName + "</span>"
+ "<span class='date'>" + repDate + "</span>"
+ "</div>"
+ "<div class='replyContent'>" + this.repCon + "</div>"

+ "<c:if test='${member != null}'>"

+ "<div class='replyFooter'>"
+ "<button type='button' class='modify' data-repNum='" + this.repNum + "'>M</button>"
+ "<button type='button' class='delete' data-repNum='" + this.repNum + "'>D</button>"
+ "</div>"

+ "</c:if>"

+ "</li>";

member 세션에 값이 있다면, 즉 로그인 상태라면 출력하고, 로그인하지 않은 상태라면 출력하지 않는 조건문입니다.

로그인 안했을 땐 버튼이 보이지 않지만, 로그인을 하면 버튼이 보이게 됩니다.

게시물 수정
  1. 안녕하세요

    view .jsp 에서 모달창을 띄우는 방법에 대해서 궁금한게 있습니다.
    body부분에 추가한 코드로 modal창이 뜨는부분에서요 ,
    textarea가 별다른 조건없이 어떻게 modal 로 바로뜨는지 궁금합니다

    아 , 그리고 $(document).on("click", ".modify", function(){
    이것과
    $(".modify").on("click",function(){
    $(".modify").onclick(function() {
    이렇게 사용하는것은 일치하는것인가요?

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

      이 게시물에선 그냥 코드만 추가해서 훌러덩(...) 넘어갔는데...
      모달만 다룬 게시물이 있습니다. 간단한 모달 레이어 만들기

      기본적인 모달은 특별한 기능이 있는게 아니라, 숨겨져있던걸(display:none;) 보여주는(display:block;) 기능입니다. textarea 역시 숨겨져있다가 나오는것입니다.

      -

      다음은 제이쿼리 이벤트부분인데, 세번째의 onclick은 자바스크립트와 관련된 속성이고, 제이쿼리에서 사용하는 속성은 click이니 click으로 설정하겠습니다.

      먼저, $(documnet).on("click", [선택자], [실행부]);, $([선택자]).on("click", [실행부]);, $([선택자]).click([실행부]); 세가지 모두 같은 실행을 합니다. 실행해보면 별다른 차이를 느끼지 못하실 겁니다.

      하지만 내부적으로는 차이가 있는데... 간단히 말하자면 이 세개의 코드는 성능이 다릅니다.

      .click()은 정적인 HTML 엘리먼트에만 접근할 수 있고, 동적으로 생성된 HTML 엘리먼트에 대해선 동작할 수 없습니다. 그렇기 때문에 동적으로 코드가 생성되는 댓글 부분에 .click()을 사용할 수 없었습니다.

      반면, .on("click", ...)는 동적으로 생성되는 HTML 엘리먼트에도 동작합니다.

      $(document).on("click", [선택자], [실행부])$([선택자]).on("click", [실행부])의 차이는... 이부부에 대해선 제가 정확히 모르는 부분이지만, 이벤트 위임에 대한 차이라고 알고 있습니다.

      삭제
  2. 안녕하세요 글 잘 보고 있습니다!

    댓글 구현을 따라하던 도중에 모달창에서 댓글을 불러올 때 게시글의 모든 댓글을 불러오는 오류가 생깁니다.
    콘솔창에서 보면 댓글 고유번호는 잘 가져오는 것 같은데 이유를 잘 모르겠어서요..
    해결 도중에 혹시 아래 코드를 좀 변경하면 될까 싶은데, 아래 코드가 이해가 잘 안되네요..

    var repCon = $(this).parent().parent().children(".replyContent").text();

    이 코드가 어떻게 동작하는지 잘 모르겠어서 댓글 남깁니다.


    답글삭제
    답글
    1. 안녕하세요? 방문해주셔서 감사합니다. 확인이 늦었네요..ㅠㅠ


      var repCon = $(this).parent().parent().children(".replyContent").text();의 의미는
      이 이미지와 아래의 설명을 같이 보시면 이해하기 편하실겁니다.

      1. 수정 버튼을 클릭했으므로, 수정 버튼이 this가 됩니다.
      2. .parent()는 현재 요소의 부모입니다.
      3. 수정 버튼의 부모는 replyFooter이고, 그 부모는 li입니다
      4. li의 자식중 클래스가 replyContent인걸 찾아서 텍스트를 반환합니다.

      즉, 현재 클릭한 버튼의 li까지 올라간다음, 다시 내려오면서 댓글 내용을 찾아들어가는건데

      사실 이 방법보다는 댓글의 고유 번호를 이용하여 찾는 방법이 더 편하지요. (새로 작성하는 게시물은 이런 방법으로 할 예정입니다)

      삭제
  3. 안녕하세요 모달 수정시에 data-repCon 값이 아닌 data-repnum값이 넘어오는데 혹시 어느부분을 확인해야 할까요>>>

    답글삭제
    답글
    1. 안녕하세요? 방문해주셔서 감사합니다.

      모달쪽에서 data-repCon을 가져오려면.. 모달부분의 데이터 전달부분를 보셔야합니다.
      게시물 본문에는 data-repCon가 없으니 아마 직접 만드신 부분같은데, 게시물 본문에서 data-repnum를 가져오는 방법것처럼, data-repCon을 가져오시면 됩니다.

      삭제