하나씩 차근차근
article thumbnail

앞에서 question 을 수정하고 삭제하는 기능을 만들었습니다.

이번에는 answer 을 수정하고 삭제할 수 있도록 해보겠습니다.

 

1. Answer 수정

answer 이 수정되는 과정은 question 이 수정되는 과정과 동일합니다.

수정 버튼 클릭 -> GET 방식 처리 -> answer_form 데이터 수정 -> POST 방식으로 처리 후 저장

1.1. 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="mb-2"> <span th:if="${answer.author != null}" th:text="${answer.author.username}"></span> </div> <div class="badge bg-light text-dark p-2 text-start"> <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>

위와 같이 수정 버튼을 누르게 되면 localhost:8080/answer/modify/{answer.id} 로 GET 방식으로 요청을 하게 됩니다.

 

1.2. AnswerService

다음으로 AnswerController 에서 사용할 메서드를 생성합니다.

<java />
package com.crud.service; ... @Service public class AnswerService { @Autowired private AnswerRepository answerRepository; ... public Answer getAnswer(Integer id) { Optional<Answer> answer = answerRepository.findById(id); // answer이 없을 경우 예외처리 필요 return answer.get(); } public void modify(Answer answer, String content) { answer.setContent(content); answer.setModifyDate(LocalDateTime.now()); answerRepository.save(answer); } }

위와 같이 id 값을 인자로 전달해 getAnswer 메서드를 통해 해당 id 의 answer 을 가져오며,

modify 메서드를 통해 answer 객체를 수정합니다.

getAnswer 메서드에서 answer 객체가 없을 경우 예외처리는 나중에 진행하겠습니다.

 

1.3. AnswerController

다음으로 수정 버튼을 클릭했을때 GET 방식으로 받은 요청을 처리하는 메서드를 작성하겠습니다.

<java />
package com.crud.controller; ... @Controller public class AnswerController { @Autowired private QuestionService questionService; @Autowired private AnswerService answerService; @Autowired private UserService userService; ... @PreAuthorize("isAuthenticated()") @GetMapping("/answer/modify/{id}") public String answerModify(AnswerForm answerForm, @PathVariable("id") Integer id, Principal principal) { Answer answer = answerService.getAnswer(id); if(!answer.getAuthor().getUsername().equals(principal.getName())) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "수정권한이 없습니다."); } answerForm.setContent(answer.getContent()); return "answer_form"; } }

principal 객체를 통해 현재 로그인한 사용자와 answer 의 author 이 다를 경우 에러를 발생시킵니다.

다음으로 POST 방식으로 요청이 왔을 경우 answerService 의 modify 메서드를 통해 

answer 를 수정하고 저장합니다.

<java />
package com.crud.controller; ... @Controller public class AnswerController { @Autowired private QuestionService questionService; @Autowired private AnswerService answerService; @Autowired private UserService userService; ... @PreAuthorize("isAuthenticated()") @PostMapping("/answer/modify/{id}") public String answerModify(@Valid AnswerForm answerForm, BindingResult bindingResult, @PathVariable("id") Integer id, Principal principal) { if(bindingResult.hasErrors()) { return "answer_form"; } Answer answer = answerService.getAnswer(id); if(!answer.getAuthor().getUsername().equals(principal.getName())) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "수정권한이 없습니다."); } answerService.modify(answer, answerForm.getContent()); return String.format("redirect:/question/detail/%s", answer.getQuestion().getId()); } }

 

1.4. answer_form

다음으로 답변 수정 버튼을 클릭했을때 수정을 할 수 있는 페이지인 answer_form 을 새로 작성합니다.

<html />
<html layout:decorate="~{layout}"> <div layout:fragment="content" class="container"> <h5 class="my-3 border-bottom pb-2">답변 수정</h5> <form th:object="${answerForm}" method="post"> <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" /> <div class="alert alert-danger" role="alert" th:if="${#fields.hasAnyErrors()}"> <div th:each="err : ${#fields.allErrors()}" th:text="${err}"/> </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>

앞에서 작성한 question_form 과 동일하게 form 태그 안에 action 속성이 없으므로

현재 호출된 URL 로 POST 방식으로 요청을 하게 됩니다.

 

1.5. 결과

다음과 같이 답변을 작성하고 수정해보겠습니다.

수정 버튼을 클릭하면 다음과 같이 답변을 수정할 수 있는 페이지로 이동하게 됩니다.

답변을 수정하고 저장하기를 누르면 답변이 수정됩니다.

 

2. Answer 삭제

Answer 을 삭제하는 기능은 Question 을 삭제하는 기능과 동일하게 만듭니다.

2.1. question_detail

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="mb-2"> <span th:if="${answer.author != null}" th:text="${answer.author.username}"></span> </div> <div class="badge bg-light text-dark p-2 text-start"> <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> ... </html>

삭제 버튼도 수정 버튼과 동일하게 현재 로그인된 사용자와 answer 의 author 을 비교해서 

같은 경우 표시되도록 합니다.

 

2.2. AnswerService

AnswerService 에 답변을 삭제하는 메서드를 생성합니다.

<java />
package com.crud.service; ... @Service public class AnswerService { @Autowired private AnswerRepository answerRepository; ... public void delete(Answer answer) { answerRepository.delete(answer); } }

 

2.3. AnswerController

다음으로 삭제 버튼을 클릭했을때 요청을 처리하는 메서드를 AnswerController 안에 만듭니다.

AnswerService 의 delete 메서드를 통해 요청이 들어오면 해당 Answer 을 삭제하도록 합니다.

<java />
package com.crud.controller; ... @Controller public class AnswerController { @Autowired private QuestionService questionService; @Autowired private AnswerService answerService; @Autowired private UserService userService; ... @PreAuthorize("isAuthenticated()") @GetMapping("/answer/delete/{id}") public String answerDelete(Principal principal, @PathVariable("id") Integer id) { Answer answer = answerService.getAnswer(id); if(!answer.getAuthor().getUsername().equals(principal.getName())) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "삭제권한이 없습니다."); } answerService.delete(answer); return String.format("redirect:/question/detail/%s", answer.getQuestion().getId()); } }

 

3. 결과

다음과 같이 삭제 버튼을 클릭하면 게시글이 삭제됩니다.

profile

하나씩 차근차근

@jeehwan_lee

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!