스프링 쇼핑몰 만들기 #19. 상품 소감(댓글) 삭제 구현
상품 소감(댓글)을 작성할 수 있으니, 이번엔 수정과 삭제입니다.
지난 게시판 만들기에서 했던것과 다르게, 이번엔 모든 CRUD(생성/조회/수정/삭제)가 에이젝스로 이루어지기 때문에 약간 다릅니다.
수정과 삭제 중, 삭제 기능을 먼저 구현하겠습니다.
view.jsp의 스크립트에서 위의 표시된 코드를 추가합니다.
+ "<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>"
스크립트에서 버튼의 HTML코드까지 입력되어진 상태입니다.
프로젝트를 실행해보면, 버튼이 생성된걸 확인할 수 있습니다.
스타일을 추가합니다.
section.replyList div.replyFooter button { font-size:14px; border: 1px solid #999; background:none; margin-right:10px; }
깔끔하고, 주변에 맞게 스타일이 적용되었습니다.
삭제 쿼리는 간단합니다. 삭제할 소감 번호(repNum)와 사용자(userId)로 구분하여 삭제할 수 있습니다.
여기서 사용자(userId)가 중요한데, 현재 로그인한 사용자가 실제로 소감을 작성한 사용자인지 구분해야합니다. 구분하지 않는다면 다른 사용자가 작성한 소감을 아무나 삭제할 수 있으면 안되겠죠.
이 쿼리는 소감 번호(repNum)의 작성자를 확인하는 쿼리입니다.
두가지 쿼리를 매퍼에 추가합니다.
<!-- 상품 소감(댓글) 삭제 -->
<delete id="deleteReply">
delete tbl_reply
where repNum = #{repNum}
and userId = #{userId}
</delete>
<!-- 아이디 체크 -->
<select id="replyUserIdCheck" resultType="String">
select userId
from tbl_reply
where repNum = #{repNum}
</select>
아이디 체크용 쿼리는 아이디(userId)만 가져오기 때문에, 결과 타입(resultType)을 ReplyVO가 아닌 String으로 사용합니다.
DAO와 Service를 작성합니다. 두개의 쿼리를 추가했으니, DAO와 Service도 각각 두개입니다.
컨트롤러를 수정하기 전에 jsp먼저 수정합니다.
view.jsp의 소감 목록 하단에 스크립트를 추가합니다.
<script>
$(document).on("click", ".delete", function(){
var data = {repNum : $(this).attr("data-repNum")};
$.ajax({
url : "/shop/view/deleteReply",
type : "post",
data : data,
success : function(){
replyList();
}
});
});
</script>
소감 목록은 스크립트로인해 생성된 동적인 HTML코드로, 일반적인 클릭 메서드 .click()
가 아니라 .on()
메서드를 사용해야합니다.
버튼의 HTML코드에있는 data-repNum
의 값을 에이젝스를 통해 컨트롤러로 전달합니다.
컨트롤러에 상품 소감 삭제용 메서드를 생성합니다.
// 상품 소감(댓글) 삭제
@ResponseBody
@RequestMapping(value = "/view/deleteReply", method = RequestMethod.POST)
public int getReplyList(ReplyVO reply, HttpSession session) throws Exception {
logger.info("post delete 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.deleteReply(reply);
result = 1;
}
return result;
}
현재 세션을 가져와서 변수 member에 저장하고, 아이디 체크용 쿼리의 결과를 가져와서 변수 userId에 저장합니다.
변수 member에서 userId부분만 추출한 값과, 변수 userId의 값을 비교합니다. 이때 비교식은 이퀄(=) 부호 2개가 아닌 이퀄 메서드 .equals()
를 사용합니다.
이렇게 현재 로그인한 사용자의 아이디와 소감을 작성한 사용자의 아이디를 비교했을 때, 결과가 참(true)이라면 삭제 작업을 진행한 뒤 변수 result에 1을 저장하고, 거짓(false)이라면 아무 작업을 하지 않고 끝냅니다.
변수 result의 결과가 0이라면 아이디가 다르기에 삭제 작업이 진행되지 않은것이며, 변수 result의 결과가 1이라면 아이디가 같아서 삭제 작업이 진행된것입니다.
에이젝스의 success 함수 내부의 코드를 수정합니다.
success : function(result){
if(result == 1) {
replyList();
} else {
alert("작성자 본인만 할 수 있습니다.");
}
}
컨트롤러부터 받아온 result의 값이 1이라면 삭제 작업이 진행되었으니 소감 목록을 다시 불러오고, 1이 아니라면 삭제 작업이 진행되지 않은것이며, 그렇다는건 로그인한 사용자와 소감을 작성한 사용자가 다른것이므로 메시지를 띄웁니다.
다른 아이디로 작성한 소감을 삭제하려고 할 경우 이렇게 메시지가 나옵니다.
자신이 작성한 소감을 지우려고하면 잘 지워지긴하는데, 실수로 누를 경우에도 지워지니 뭔가 대책이 필요합니다.
이때 컨펌(confirm) 메서드를 사용합니다.
컨펌 메서드는 내부의 텍스트를 사용자에게 보여주며 확인 버튼과 취소 버튼을 같으 보여줍니다. 확인 버튼을 클릭하면 참(true)를 반환하고, 취소 버튼을 클릭하면 거짓(false)를 반환합니다.
컨펌 메시지가 있는 동안 다른 작업을 할 수 없으므로, 사용자가 실수로 삭제 버튼을 누르더라도 바로 삭제되는 일을 방지할 수 있습니다.
다음은 로그인하지 않은 상태에서 버튼을 눌렀을 때입니다.
컨트롤러에서 세션을 가져온 뒤 작업하는데, 로그인하지 않았다면 모든 세션이 null이므로 에러가 발생됩니다. 에이젝스는 에러가 발생할 경우에 실행될 코드를 따로 설정할 수 있습니다.
$.ajax({
url : "/shop/view/deleteReply",
type : "post",
data : data,
success : function(result){
if(result == 1) {
replyList();
} else {
alert("작성자 본인만 할 수 있습니다.");
}
},
error : function(){
alert("로그인하셔야합니다.")
}
});
로그인하지 않은 상태에서 삭제 버튼을 클릭할 경우, 이렇게 로그인하라는 메시지가 출력됩니다.
작성자가 댓글을 삭제했습니다.
답글삭제아 또하나 질문이 있습니다..
삭제{ : } 이런식으로 json형식의 데이터를 쓸때에
controller vo 부분에 @RequestBody 를 쓴다고 알고있는데 위에서는 안써도 잘 적용이 되는데 ,
ajax 에서 dataType을 json으로 지정을 안해줘서 그런건가요?
안녕하세요?
삭제@RequestBody를 사용하지않아도 정상적으로 작동하는 이유는 스프링의 자동 매핑 기능이기 때문입니다.
뷰에서 컨트롤러에 전달되는 데이터의 형태가, 컨트롤러가 받으려는 매개변수의 형태와 같기 때문에 별다른 작업이 없어도 잘 적용되는겁니다. 물론, 하나라도 잘못되었다면 에러가 발생하거나 매핑이 안되어서 null이 뜨겠지요.
뷰에서 컨트롤러에 전달한 데이터와, 컨트롤러가 받으려는 매개변수의 데이터형이 일치하지 않거나 다수일 경우엔 꼭 @RequestBody를 넣어줘야합니다.
스프링의 자동 매핑 기능이 있다곤하지만, 코드를 직접 보는 개발자를 위해 @RequestBody를 넣어주는게 바람직하다고 생각합니다. 정작 저는 안 넣었지만요-_-;
안녕하세요, 항상 잘 보고 있습니다.
답글삭제저는 스프링 시큐리티를 접목해서 HttpSession 객체를 불러와서 처리하지 않고, Principal 객체를 불러와서 유저인지 비교를 하고 있는데요,
그런데 제 문제는 이게 아니라, 접속한 유저인지는 int result = 1 이 나오는 것을 콘솔창으로 확인을 했는데,
ajax처리에서 success function(result)에서 매개변수로 result=1이 안받아지는것 같습니다. XMLObject로 뜨는데요,
JSON.parse(JSON.stringify(result)) 이런식으로도 해보고 반대로도 해봤지만 result = 1 이 안나오네요.
사실 이 게시글에서 장바구니 처리에서도 그렇게 result 값이 안받아져서 다르게 처리를 했는데, 왜 이러는지 궁금합니다.
항상 잘 보고있습니다, 감사합니다.
안녕하세요, 위의 질문을 올린 사람입니다.
답글삭제console.log(result) 를 했을때 <integer> 1 </integer> 로 출력이 잘되는데,
if문에서 result=1 인 조건 결과를 잘 수행하는데
자꾸 else문에서 alert('본인 댓글만 작성 가능') 을 반환하는데요,
실제로 새로 고침을 해보면 삭제는 이루어져있습니다.
그런데 if문에 return을 넣어봐도 자꾸 else문의 alert창을 던지는데,
혹시 쿠키가 너무 많이 쌓여서 그런가 해서 확인해봐도 그건 아닌것 같고... ㅠㅠ 미치겠네요
안녕하세요 저도 지금 security 사용해서 구현해보려고 하는 중인데 혹시 괜찮으시면 코드소스 보여주실 수 있으신가요? ㅠㅠ
삭제안녕하세요? 방문해주셔서 감사합니다.
삭제게시물 본문에 깃허브 링크를 걸어두었으며, 깃허브에서 모든 코드를 보실 수 있습니다.
아이디를 가져오는 쿼리를 따로 작성하는 이유가 있을까요?? replyvo에서 getuserId로 가져와서 비교하는건 안되는지 궁금합니다!
답글삭제안녕하세요? 방문해주셔서 감사합니다.
삭제말씀하신대로, 게시물 본문 처럼 따로따로 작업하지 않고 작업하셔도 됩니다.