(16-2) 기능구현 - 수정과 삭제 (답변)
답변을 수정하고 삭제하는 기능을 구현하자
이전포스팅의 기능과 거의 비슷한 방법
시작하기 전에
개요 : 질문과 답변을 할 수 있는 게시판 서비스를 스프링부트를 통해 만들어 본다.
학습사이트 : https://wikidocs.net/book/7601
예제 코드 : https://github.com/pahkey/sbb
답변 수정
답변 수정 버튼
답변 수정 버튼을 추가한다
- question_detail.html 수정
<!-- 경로 : sbb/src/main/resources/templates/question_detail.html -->
<html layout:decorate="~{layout}">
(... 생략 ...)
<!-- 답변 반복 시작 -->
<div class="card my-3" th:each="answer : ${question.answerList}">
<div class="card-body">
<div class="card-text" style="white-space: pre-line;" th:text="${answer.content}"></div>
<div class="d-flex justify-content-end">
<!-- 수정 버튼 -->
<div class="badge bg-light text-dark p-2 text-start">
<div class="mb-2">
<span th:if="${answer.author != null}" th:text="${answer.author.username}"></span>
</div>
<div th:text="${#temporals.format(answer.createDate, 'yyyy-MM-dd HH:mm')}"></div>
</div>
</div>
<div class="my-3">
<a th:href="@{|/answer/modify/${answer.id}|}" class="btn btn-sm btn-outline-secondary"
sec:authorize="isAuthenticated()"
th:if="${answer.author != null and #authentication.getPrincipal().getUsername() == answer.author.username}"
th:text="수정"></a>
</div>
</div>
</div>
<!-- 답변 반복 끝 -->
(... 생략 ...)
</html>
수정 버튼을 누르면 /answer/modify/답변ID
형태의 URL이 GET 방식으로 요청된다.
- SBB테스트
수정 버튼이 생겼다!
답변 수정 기능 구현
버튼을 만들었으니 기능을 구현하자.
- AnswerService.java 수정
// 경로 : sbb/src/main/java/com/mysite/sbb/answer/AnswerService.java
package com.mysite.sbb.answer;
(... 생략 ...)
import java.util.Optional;
import com.mysite.sbb.DataNotFoundException;
(... 생략 ...)
@RequiredArgsConstructor
@Service
public class AnswerService {
(...)
// 답변 조회 메서드
// 답변을 수정, 삭제하기 위해 본인의 ID와 같은지 검사가 필요
public Answer getAnswer(Integer id) {
Optional<Answer> answer = this.answerRepository.findById(id);
if (answer.isPresent()) {
return answer.get();
} else {
throw new DataNotFoundException("answer not found");
}
}
// 답변 수정 메서드
public void modify(Answer answer, String content) {
answer.setContent(content);
answer.setModifyDate(LocalDateTime.now());
this.answerRepository.save(answer);
}
}
답변을 수정, 삭제하기 위한 getAnswer메서드와, 답변을 수정하는 modify메서드 추가.
- AnswerController.java 수정
// 경로 : sbb/src/main/java/com/mysite/sbb/answer/AnswerController.java
package com.mysite.sbb.answer;
(... 생략 ...)
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.server.ResponseStatusException;
(... 생략 ...)
@RequestMapping("/answer") // URL 프리픽스
@RequiredArgsConstructor
@Controller
public class AnswerController {
(... 생략 ...)
// 답변 수정 URL 컨트롤
@PreAuthorize("isAuthenticated()")
@GetMapping("/modify/{id}")
public String answerModify( AnswerForm answerForm,
@PathVariable("id") Integer id,
Principal principal) {
Answer answer = this.answerService.getAnswer(id);
if (!answer.getAuthor().getUsername().equals(principal.getName())) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "수정권한이 없습니다.");
}
answerForm.setContent(answer.getContent());
return "answer_form";
}
}
답변 수정시 기존의 내용이 필요하므로 AnswerForm 객체에 조회한 값을 저장하고,
저장한 값을 answer_form.html템플릿에서 수정할수 있게 한다.
아직 템플릿을 만들지 않았으므로 다음으로는 템플릿을 만들어보자.
- answer_form.html 생성
<!-- 경로 : /sbb/src/main/resources/templates/answer_form.html -->
<html layout:decorate="~{layout}">
<div layout:fragment="content" class="container">
<h5 class="my-3 border-bottom pb-2">답변 수정</h5>
<!-- 답변 전달 (POST) -->
<form th:object="${answerForm}" method="post">
<!-- CSRF 수동추가 -->
<input type="hidden"
th:name="${_csrf.parameterName}"
th:value="${_csrf.token}" />
<div th:replace="form_errors :: formErrorsFragment"></div>
<div class="mb-3">
<label for="content" class="form-label">내용</label>
<textarea th:field="*{content}"
class="form-control"
rows="10"></textarea>
</div>
<input type="submit" value="저장하기" class="btn btn-primary my-2">
</form>
</div>
</html>
수정할 값을 POST방식으로 전달한다.
따라서 Controlloer 수정도 필요하다.
- AnswerController.java 수정
// 경로 : sbb/src/main/java/com/mysite/sbb/answer/AnswerController.java
package com.mysite.sbb.answer;
(... 생략 ...)
@RequestMapping("/answer") // URL 프리픽스
@RequiredArgsConstructor
@Controller
public class AnswerController {
(... 생략 ...)
// 답변 수정 (POST) 처리
@PreAuthorize("isAuthenticated()")
@PostMapping("/modify/{id}")
public String answerModify(@Valid AnswerForm answerForm, BindingResult bindingResult,
@PathVariable("id") Integer id, Principal principal) {
if (bindingResult.hasErrors()) {
return "answer_form";
}
Answer answer = this.answerService.getAnswer(id);
if (!answer.getAuthor().getUsername().equals(principal.getName())) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "수정권한이 없습니다.");
}
this.answerService.modify(answer, answerForm.getContent());
// 답변 완료 후 질문 상세 페이지로 돌아감
return String.format("redirect:/question/detail/%s", answer.getQuestion().getId());
}
}
answer_form의 POST요청을 처리하는 answerModify 메서드 추가
수정 일시 표시
- question_detail.html 수정
<!-- 경로 : sbb/src/main/resources/templates/question_detail.html -->
<html layout:decorate="~{layout}">
<div layout:fragment="content" class="container my-3">
(... 생략 ...)
<!-- 답변 반복 시작 -->
<div th:each="answer : ${question.answerList}">
<a th:id="|answer_${answer.id}|"></a>
<div class="card my-3">
<div class="card-body">
<div class="d-flex justify-content-end">
<!-- 수정 일시 표시 -->
<div th:if="${answer.modifyDate != null}"
class="badge bg-light text-dark p-2 text-start mx-3">
<div class="mb-2">modified at</div>
<div th:text="${#temporals.format(answer.modifyDate, 'yyyy-MM-dd HH:mm')}"></div>
</div>
<!-- 글쓴이, 작성시간 표시 -->
<div class="badge bg-light text-dark p-2 text-start">
<div class="mb-2">
<span th:if="${answer.author != null}"
th:text="${answer.author.username}"></span>
</div>
<div th:text="${#temporals.format(answer.createDate,
'yyyy-MM-dd HH:mm')}"></div>
</div>
</div>
<!-- 답변 수정 버튼 -->
<div class="my-3">
<a th:href="@{|/answer/modify/${answer.id}|}"
class="btn btn-sm btn-outline-secondary"
sec:authorize="isAuthenticated()"
th:if="${answer.author != null and #authentication.getPrincipal().getUsername() == answer.author.username}"
th:text="수정"></a>
</div>
</div>
</div>
</div>
<!-- 답변 반복 끝 -->
(... 생략 ...)
</html>
질문 수정일시를 수정해준 것과 똑같이 해준다.
SBB테스트
- 수정 템플릿
- 수정 확인
잘 수정된다!
답변 삭제
답변 삭제 버튼
- question_detail.html 수정
<!-- 경로 : sbb/src/main/resources/templates/question_detail.html -->
<html layout:decorate="~{layout}">
(... 생략 ...)
<!-- 답변 반복 시작 -->
<div th:each="answer : ${question.answerList}">
<a th:id="|answer_${answer.id}|"></a>
<div class="card my-3">
<div class="card-body">
<div class="d-flex justify-content-end">
<!-- 수정 일시 표시 -->
<div th:if="${answer.modifyDate != null}"
class="badge bg-light text-dark p-2 text-start mx-3">
<div class="mb-2">modified at</div>
<div th:text="${#temporals.format(answer.modifyDate, 'yyyy-MM-dd HH:mm')}"></div>
</div>
<!-- 글쓴이, 작성시간 표시 -->
<div class="badge bg-light text-dark p-2 text-start">
<div class="mb-2">
<span th:if="${answer.author != null}"
th:text="${answer.author.username}"></span>
</div>
<div th:text="${#temporals.format(answer.createDate,
'yyyy-MM-dd HH:mm')}"></div>
</div>
</div>
<!-- 답변 수정, 삭제 버튼 -->
<div class="my-3">
<!-- 수정 -->
<a th:href="@{|/answer/modify/${answer.id}|}"
class="btn btn-sm btn-outline-secondary"
sec:authorize="isAuthenticated()"
th:if="${answer.author != null and #authentication.getPrincipal().getUsername() == answer.author.username}"
th:text="수정"></a>
<!-- 삭제 -->
<a href="javascript:void(0);" th:data-uri="@{|/answer/delete/${answer.id}|}"
class="delete btn btn-sm btn-outline-secondary"
sec:authorize="isAuthenticated()"
th:if="${answer.author != null and #authentication.getPrincipal().getUsername() == answer.author.username}"
th:text="삭제"></a>
</div>
</div>
</div>
</div>
<!-- 답변 반복 끝 -->
(... 생략 ...)
</html>
수정 오른쪽에 삭제 버튼 추가.
질문 삭제와 마찬가지로 버튼 class = delete이므로 자바스크립트 알림창이 띄워진다.
답변 삭제 기능
- AnswerService.java 수정
// 경로 : sbb/src/main/java/com/mysite/sbb/answer/AnswerService.java
package com.mysite.sbb.answer;
(... 생략 ...)
@RequiredArgsConstructor
@Service
public class AnswerService {
(... 생략 ...)
// 답변 삭제 메서드
public void delete(Answer answer) {
this.answerRepository.delete(answer);
}
}
입력으로 받은 Answer 객체를 사용하여 답변을 삭제하는 delete 메서드 추가
- AnswerController.java 수정
// 경로 : sbb/src/main/java/com/mysite/sbb/answer/AnswerController.java
package com.mysite.sbb.answer;
(... 생략 ...)
@RequestMapping("/answer") // URL 프리픽스
@RequiredArgsConstructor
@Controller
public class AnswerController {
(... 생략 ...)
// 답변 삭제 (GET)
@PreAuthorize("isAuthenticated()")
@GetMapping("/delete/{id}")
public String answerDelete(
Principal principal,
@PathVariable("id") Integer id) {
Answer answer = this.answerService.getAnswer(id);
if (!answer.getAuthor().getUsername().equals(principal.getName())) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "삭제권한이 없습니다.");
}
this.answerService.delete(answer);
return String.format("redirect:/question/detail/%s", answer.getQuestion().getId());
}
}
답변 삭제시 요청되는 GET방식의 URL을 처리하는 answerDelete 메서드 추가.
SBB테스트
- 삭제알림
- 삭제 확인
잘 삭제된다
Leave a comment