[Spring Boot] 삭제하기

DANI·2023년 10월 16일
0
post-thumbnail

💻 게시판의 질문, 답변에 삭제 기능을 구현해보자

삭제 기능엔 무엇이 필요할까?

✅ 템플릿
삭제버튼
✅ 서비스
delete 메소드(GETMAPPING)
✅ 컨트롤러
삭제버튼과 매핑




📩 1. 질문 삭제 하기

💾 삭제 버튼 만들기(question_detail.html 템플릿 수정)

✅ 수정버튼 우측에 삭제버튼을 추가해보자!

<a href="javascript:void(0);" th:data-uri="@{|/question/delete/${question.id}|}"
                class="delete badge rounded-pill bg-dark text-white" sec:authorize="isAuthenticated()"
                th:if="${question.author != null and #authentication.getPrincipal().getUsername() == question.author.username}"
                th:text="삭제"></a>

🔴 수정 버튼 : th:href="@{|/question/modify/${question.id}|}"

🔵 삭제 버튼 : href="javascript:void(0);" th:data-uri="@{|/question/delete/${question.id}|}"

삭제 버튼은 수정 버튼과는 달리 href 속성 값을 javascript:void(0)로 설정했다. 그리고 삭제를 실행할 URL을 얻기 위해 th:data-uri 속성을 추가하고, <삭제> 버튼이 눌리는 이벤트를 확인할 수 있도록 class 속성에 "delete" 항목을 추가해 주었다.

data-uri 속성은 클릭 이벤트 발생시 this.dataset.uri와 같이 사용하여 그 값을 얻을 수 있다.

why? href에 삭제 URL을 직접 사용하지 않는 이유?


버튼을 클릭했을 때 "정말로 삭제하시겠습니까?"와 같은 확인 절차가 필요하기 때문에



💾 삭제 이벤트 만들기(question_detail.html 템플릿 수정)

✅ 삭제 버튼을 누르면 "정말로 삭제하시겠습니까?"라는 확인창이 뜨는 자바스크립트 추가하기!

<!-- 가장 아래에 <script>엘리먼트를 삽입한다 -->
<script layout:fragment="script" type='text/javascript'>
const delete_elements = document.getElementsByClassName("delete");
Array.from(delete_elements).forEach(function(element) {
    element.addEventListener('click', function() {
        if(confirm("정말로 삭제하시겠습니까?")) {
            location.href = this.dataset.uri;
        };
    });
});
</script>
<!-- 가장 아래에 <script>엘리먼트를 삽입한다 -->
</html> 

delete라는 클래스를 포함하는 컴포넌트를 클릭하면 "정말로 삭제하시겠습니까?"라는 질문을 하고, true일 때 data-uri(@{|/question/delete/${question.id}|})를 호출 하고 false일때는 아무런 일도 발생하지 않는다.


💾 layout.html 템플릿 레이아웃 수정

✅ 가장 하단에 script 블록 구현

<!doctype html>
<html lang="ko">
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- Bootstrap CSS -->
    <link rel="stylesheet" type="text/css" th:href="@{/bootstrap.min.css}">
    <!-- sbb CSS -->
    <link rel="stylesheet" type="text/css" th:href="@{/style.css}">
    <title>Hello, sbb!</title>
</head>
<body>
<!-- 기본 템플릿 안에 삽입될 내용 Start -->
<nav th:replace="~{navbar :: navbarFragment}"></nav>
<th:block layout:fragment="content"></th:block>
<!-- 기본 템플릿 안에 삽입될 내용 End -->
<script th:src="@{/bootstrap.min.js}"></script>
<th:block layout:fragment="script"></th:block>
</body>
</html>

✅ 추가 : <th:block layout:fragment="script"></th:block>

가장 아래에 엘리먼트를 추가했다. 왜냐하면 화면 렌더링이 완료된 후에 자바스크립트가 실행되기 때문이다. 화면 렌더링이 완료되지 않은 상태에서 자바스크립트를 실행하면 오류가 발생할수도 있고 화면 로딩이 지연되는 문제가 발생할 수도 있다.


💾 QuestionService에 delete 메소드 추가

✅ delete 메소드 추가

public void delete(Question question) {
		this.questionRepository.delete(question);
}

💾 QuestionController에 @{|/question/delete/${question.id}|} 매핑하기

✅ delete 메소드 추가

@PreAuthorize("isAuthenticated()")
@GetMapping("/delete/{id}")
public String questionDelete(Principal principal, @PathVariable("id") Integer id) {
         // id를 통해 질문정보를 저장한다.
	     Question question = this.questionService.getQuestion(id);

         // 질문의 작성자의 이름과 접속한 객체의 이름이 동일하지 않으면 에러 발생
	     if(!question.getAuthor().getUsername().equals(principal.getName())) {
	    	 throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "삭제권한이 없습니다.");
	     }
          // 작성자와 접속한 객체가 동일하면 삭제메소드 실행
	      this.questionService.delete(question);
	      return "redirect:/"; // 루트페이지로 리다이렉트
}



💻 실행화면

삭제 버튼이 생성되었다!


"정말로 삭제하시겠습니까?" 팝업창이 생성된다! 확인을 눌러보자!


삭제되었고, 루트페이지로 리다이렉트 된다!





📩 2. 답변 삭제 하기

💾 답변 버튼 만들기(question_detail.html 템플릿 수정)

✅ 답변 수정버튼 우측에 삭제버튼을 추가해보자!

<a href="javascript:void(0);" th:data-uri="@{|/answer/delete/${answer.id}|}"
   class="delete badge rounded-pill bg-dark text-white"
   sec:authorize="isAuthenticated()"
   th:if="${answer.author != null and #authentication.getPrincipal().getUsername() == answer.author.username}"
   th:text="삭제"></a>

매핑 주소를 제외하고 질문 삭제 버튼과 유사하다.


💾 AnswerService에 delete 메소드 추가

✅ delete 메소드 추가

public void delete(Answer answer) {
    	this.answerRepository.delete(answer);
}

질문 delete메소드와 유사하다


💾 AnswerController에 @{|/answer/delete/${answer.id}|} 매핑하기

✅ delete 메소드 추가

@PreAuthorize("isAuthenticated()")
@GetMapping("/delete/{id}")
public String answerDelete(Principal principal, @PathVariable("id") Integer id) {
	    // 입력받은 id로 답변을 조회한 뒤 저장한다.
        Answer answer = this.answerService.getAnswer(id);
        // 현재 uesr와 답변의 작성자가 동일하지 않을경우 예외 발생
	    if (!answer.getAuthor().getUsername().equals(principal.getName())) {
	        throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "삭제권한이 없습니다.");
	    }
        // 동일할 경우 delete 메소드 실행
	    this.answerService.delete(answer);
        // 삭제하게 된 후 현재 질문 상세 페이지로 리다이렉트한다.
	    return String.format("redirect:/question/detail/%s", answer.getQuestion().getId());
	}



💻 실행화면

삭제 버튼이 생성되었다!

"정말로 삭제하시겠습니까?" 팝업창이 생성된다! 확인을 눌러보자!


삭제되었고, 상세페이지로 리다이렉트 된다!






✨ 이번 챕터에서 배운 부분

✅ 자바 스크립트를 통해 click event 발생 시키기
✅ layout 상속

📝 공부할 부분

✅ 자바 스크립트 click event 공부하기
✅ layout 상속 복습

0개의 댓글