점프 투 스프링부트를 참고하여 학습,작성하였습니다.
1.수정 속성추가
Question, Answer 모델에 수정시간 속성을 추가해준다.
private LocalDateTime modifyDate;
2.수정버튼 생성
수정으로 이동하는 버튼을 생성해준다.
이때 sec:authorize으로 사용자가 로그인임을 체크해주고
다음 조건문으로 작성자가 로그인한 사용자와 같은지 체크한다.
<div class="my-3">
<a th:href="@{|/question/modify/${question.id}|}" class="btn btn-sm btn-outline-secondary"
sec:authorize="isAuthenticated()"
th:if="${question.author != null and #authentication.getPrincipal().getUsername() == question.author.username}"
th:text="수정"></a>
</div>
3.컨트롤러 수정
수정url로 매핑해준다. GET요청으로 이동할때다.
@PreAuthorize("isAuthenticated()")으로 로그인되어있는지 확인한다.
만약 글쓴이와 로그인사용자가 다르면 오류가 발생하게한다.
questionForm 객체의 subject 필드를 현재 질문의 제목으로 설정,
questionForm 객체의 content 필드를 현재 질문의 내용으로 설정후 뷰를 반환한다.
@PreAuthorize("isAuthenticated()")
@GetMapping(value = "/question/modify/${question.id}")
public String questionModify(QuestionForm questionForm, @PathVariable("id") Integer id, Principal principal) {
Question question = this.questionService.getQuestion(id);
if(!question.getAuthor().getUsername().equals(principal.getName())) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "수정권한이 없습니다.");
}
questionForm.setSubject(question.getSubject());
questionForm.setContent(question.getContent());
return "question_form";
}
4.질문 등록 템플릿 수정
본래 action="@{/question/create}"으로 post하도록 되어있었지만
만약 폼이 action 속성없이 POST로 폼을 전송한다면 현재 URL을 기준으로 전송하게된다.
Spring Security가 제공하는 CSRF 보호 기능을 사용한다.
<form th:action="@{/question/create}" th:object="${questionForm}" method="post">
=>
<form th:object="${questionForm}" method="post">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
5.서비스 추가
Question 서비스에 modify 메서드를 추가한다.
public void modify(Question question, String subject, String content) {
question.setSubject(subject);
question.setContent(content);
question.setModifyDate(LocalDateTime.now());
this.questionRepository.save(question);
}
6.컨트롤러 수정
수정을 받아 POST 되는 메서드를 추가한다.
BindingResult bindingResult로 유효성 검사 결과를 체크한다.
@PreAuthorize("isAuthenticated()")
@PostMapping("/modify/{id}")
public String questionModify(@Valid QuestionForm questionForm, BindingResult bindingResult,
Principal principal, @PathVariable("id") Integer id) {
if (bindingResult.hasErrors()) {
return "question_form";
}
Question question = this.questionService.getQuestion(id);
if (!question.getAuthor().getUsername().equals(principal.getName())) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "수정권한이 없습니다.");
}
this.questionService.modify(question, questionForm.getSubject(), questionForm.getContent());
return String.format("redirect:/question/detail/%s", id);
}
7.삭제기능 버튼생성
<a href="javascript:void(0);" th:data-uri="@{|/question/delete/${question.id}|}"
class="delete btn btn-sm btn-outline-secondary" sec:authorize="isAuthenticated()"
th:if="${question.author != null and #authentication.getPrincipal().getUsername() == question.author.username}"
th:text="삭제"></a>
8.Base javascript 추가
base.html에 자바스크립트블록을 추가하고
버튼을 누르면 삭제할것인지 다시물어보는 스크립트를 추가한다.
</head>
<body>
<!-- 네비게이션바 -->
<nav th:replace="~{navbar :: navbarFragment}"></nav>
<!-- 기본 템플릿 안에 삽입될 내용 Start -->
<th:block layout:fragment="content"></th:block>
<!-- 기본 템플릿 안에 삽입될 내용 End -->
<!-- 자바스크립트 Start -->
<th:block layout:fragment="script"></th:block>
<!-- 자바스크립트 End -->
</body>
</html>
<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>
9.서비스 추가
Question객체를 삭제하는 delete 메소드를만든다.
public void delete(Question question) {
this.questionRepository.delete(question);
}
10.컨트롤러 추가
GET으로 매핑한다.
글쓴이와 사용자가 같다면 서비스를 통해 질문객체를 삭제시키고 루트url로 이동시킨다.
@PreAuthorize("isAuthenticated()")
@GetMapping("/delete/{id}")
public String questionDelete(Principal principal, @PathVariable("id") Integer 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:/";
}
11.답변 수정,삭제기능 추가
11-1.버튼
<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>
<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>
11-2.서비스
답변 조회, 수정,삭제 서비스 추가
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);
}
public void delete(Answer answer) {
this.answerRepository.delete(answer);
}
11-3.템플릿생성
답변 수정을 담당할 answer_form.html 생성
<html layout:decorate="~{base}">
<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 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>
11-4.컨트롤러 생성
수정 GET , 수정 POST, 삭제 POST 매핑을 수행하는 메소드 생성
@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";
}
@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());
}
@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());
}
'BackEnd > SpringBoot' 카테고리의 다른 글
[Spring Boot] 추천 기능 (0) | 2024.07.08 |
---|---|
[Spring Boot] 글쓴이 항목 추가 (0) | 2024.07.05 |
[Spring Boot] 로그인, 로그아웃 (0) | 2024.07.05 |