앞에서 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. 결과
다음과 같이 삭제 버튼을 클릭하면 게시글이 삭제됩니다.


'Spring > Springboot 실습' 카테고리의 다른 글
게시판 만들기 - 글 수정 및 삭제(1) (0) | 2023.02.02 |
---|---|
게시판 만들기 - 글쓴이 표시(2) (0) | 2023.01.26 |
게시판 만들기 - 글쓴이 표시(1) (0) | 2023.01.26 |
게시판 만들기 - 로그아웃 (0) | 2023.01.25 |
게시판 만들기 - 로그인 (0) | 2023.01.25 |