03L. ์ต์ปค
03L. ์ต์ปค ๊ด๋ จ
์ด์ SBB๊ฐ ์ ์ ์์ฑ ๋์ด๊ฐ๊ณ ์๋ค. ์ด๋ฒ์๋ ๋ ๋ง์ ๊ธฐ๋ฅ์ ์ถ๊ฐํ๊ธฐ ์ ์ ๋ฐ๊ฒฌ๋ ๋ช ๊ฐ์ง ๋ฌธ์ ์ ์ ํด๊ฒฐํ๋ ค๊ณ ํ๋ค.
๋ฐ๊ฒฌ๋ ๋ฌธ์ ์ ์ ๋ต๊ธ์ ์์ฑํ๊ฑฐ๋ ์์ ํ ํ์ ํญ์ ํ์ด์ง ์๋จ์ผ๋ก ์คํฌ๋กค์ด ์ด๋๋๊ธฐ ๋๋ฌธ์ ๋ณธ์ธ์ด ์์ฑํ ๋ต๋ณ์ ํ์ธํ๋ ค๋ฉด ๋ค์ ์คํฌ๋กค์ ๋ด๋ ค์ ํ์ธํด์ผ ํ๋ค๋ ์ ์ด๋ค. ์ด ๋ฌธ์ ๋ ๋ต๋ณ์ ์ถ์ฒํ ๊ฒฝ์ฐ์๋ ๋์ผํ๊ฒ ๋ฐ์ํ๋ค.
Ajax์ ๊ฐ์ ๋น๋๊ธฐ ํต์ ๊ธฐ์ ์ ์ฌ์ฉํ์ฌ ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์๋ ์์ง๋ง ์ฌ๊ธฐ์๋ ๋ณด๋ค ์ฌ์ด ๋ฐฉ๋ฒ์ผ๋ก ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํด ๋ณด์. HTML์๋ URL ํธ์ถ์ ์ํ๋ ์์น๋ก ์ด๋์์ผ ์ฃผ๋ ์ต์ปค(anchor
) ํ๊ทธ๊ฐ ์๋ค. ์๋ฅผ ๋ค์ด HTML ์ค๊ฐ์ <a id="test"></a>
๋ผ๋ ์ต์ปค ํ๊ทธ๊ฐ ์๋ค๋ฉด ์ด HTML์ ํธ์ถํ๋ URL ๋ค์ #test
๋ผ๊ณ ๋ถ์ฌ์ฃผ๋ฉด ํด๋น ํ์ด์ง๊ฐ ํธ์ถ๋๋ฉด์ ํด๋น ์ต์ปค๋ก ์คํฌ๋กค์ด ์ด๋๋๋ค.
๋ต๋ณ๋ฑ๋ก, ๋ต๋ณ์์ , ๋ต๋ณ์ถ์ฒ ์ ์ต์ปค ํ๊ทธ๋ฅผ ์ด์ฉํ์ฌ ์ํ๋ ์์น๋ก ์ด๋ํ ์ ์๋๋ก ์์ ํด ๋ณด์.
๋ต๋ณ ์ต์ปค ์ถ๊ฐ
๋จผ์ ๋ต๋ณ ์์ฑ, ์์ ์์ ์ด๋ํด์ผํ ์ต์ปค ํ๊ทธ๋ฅผ ์ง๋ฌธ ์์ธ ํ ํ๋ฆฟ์ ์ถ๊ฐํ์.
ํ์ผ๋ช :
/sbb/src/main/resources/templates/
question_detail.html
<!-- (... ์๋ต ...) -->
<!-- ๋ต๋ณ์ ๊ฐฏ์ ํ์ -->
<h5 class="border-bottom my-3 py-2"
th:text="|${#lists.size(question.answerList)}๊ฐ์ ๋ต๋ณ์ด ์์ต๋๋ค.|"></h5>
<!-- ๋ต๋ณ ๋ฐ๋ณต ์์ -->
<div class="card my-3" th:each="answer : ${question.answerList}">
<a th:id="|answer_${answer.id}|"></a>
<div class="card-body">
<!-- (... ์๋ต ...) -->
๋ต๋ณ์ด ๋ฐ๋ณต๋์ด ํ์๋๋ th:each
๋ฐ๋ก ๋ค์์ <a th:id="|answer_${answer.id}|"></a>
์ ๊ฐ์ด ์ต์ปค ํ๊ทธ๋ฅผ ์ถ๊ฐํ๋ค. ์ต์ปค ํ๊ทธ์ id
์์ฑ์ ์ ์ผํ ๊ฐ์ด์ด์ผ ํ๋ฏ๋ก answer_{{ answer.id }}
์ ๊ฐ์ด ๋ต๋ณ id
๋ฅผ ์ฌ์ฉํ๋ค.
๋ต๋ณ redirect
์ด์ ๋ต๋ณ์ ๋ฑ๋กํ๊ฑฐ๋ ์์ ํ ๋ ์์์ ์ง์ ํ ์ต์ปค ํ๊ทธ๋ก ์ด๋ํ๋๋ก ์ฝ๋๋ฅผ ์์ ํ์. ๋ค์์ ๋ต๋ณ ๋ฑ๋ก ๋๋ ๋ต๋ณ ์์ ์ ํ ๋ค ์ฌ์ฉํ๋ ๊ธฐ์กด ์ฝ๋์ ์ผ๋ถ์ด๋ค.
return String.format("redirect:/question/detail/%s", answer.getQuestion().getId());
์ฌ๊ธฐ์ ์ต์ปค๋ฅผ ํฌํจํ๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
return String.format("redirect:/question/detail/%s#answer_%s",
answer.getQuestion().getId(), answer.getId());
๋ฆฌ๋ค์ด๋ ํธ๋๋ ์ง๋ฌธ ์์ธ URL์ #answer_2
์ ๊ฐ์ ์ต์ปค๋ฅผ ์ถ๊ฐํ ๊ฒ์ด๋ค. ์ด๋ฌํ ๋ฐฉ๋ฒ์ผ๋ก ๋ต๋ณ ์์ฑ, ์์ , ์ถ์ฒ์ ๋ฆฌ๋ค์ด๋ ํธ ๋ถ๋ถ์ ๋ณ๊ฒฝํ์.
AnswerService
์ปจํธ๋กค๋ฌ์์ ๋ต๋ณ์ด ๋ฑ๋ก๋ ์์น๋ก ์ด๋ํ๊ธฐ ์ํด์๋ ๋ต๋ณ ๊ฐ์ฒด๊ฐ ๋ฐ๋์ ํ์ํ๋ค. ๋ฐ๋ผ์ ๋ค์๊ณผ ๊ฐ์ด AnswerService
๋ฅผ ๋จผ์ ์์ ํด์ผ ํ๋ค.
ํ์ผ๋ช :
/sbb/src/main/java/com/mysite/sbb/answer/
AnswerService.java
// (... ์๋ต ...)
public class AnswerService {
// (... ์๋ต ...)
public Answer create(Question question, String content, SiteUser author) {
Answer answer = new Answer();
answer.setContent(content);
answer.setCreateDate(LocalDateTime.now());
answer.setQuestion(question);
answer.setAuthor(author);
this.answerRepository.save(answer);
return answer;
}
// (... ์๋ต ...)
}
AnswerController
๊ทธ๋ฆฌ๊ณ ์ปจํธ๋กค๋ฌ์ ๋ฆฌ๋ค์ด๋ ํธ ์ฝ๋๋ค์ ๋ค์๊ณผ ๊ฐ์ด ์์ ํ์.
ํ์ผ๋ช :
/sbb/src/main/java/com/mysite/sbb/answer/
AnswerController.java
// (... ์๋ต ...)
public class AnswerController {
// (... ์๋ต ...)
@PreAuthorize("isAuthenticated()")
@PostMapping("/create/{id}")
public String createAnswer(Model model, @PathVariable("id") Integer id,
@Valid AnswerForm answerForm, BindingResult bindingResult, Principal principal) {
Question question = this.questionService.getQuestion(id);
SiteUser siteUser = this.userService.getUser(principal.getName());
if (bindingResult.hasErrors()) {
model.addAttribute("question", question);
return "question_detail";
}
Answer answer = this.answerService.create(question,
answerForm.getContent(), siteUser);
return String.format("redirect:/question/detail/%s#answer_%s",
answer.getQuestion().getId(), answer.getId());
}
// (... ์๋ต ...)
@PreAuthorize("isAuthenticated()")
@PostMapping("/modify/{id}")
public String answerModify(@Valid AnswerForm answerForm, @PathVariable("id") Integer id,
BindingResult bindingResult, 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_%s",
answer.getQuestion().getId(), answer.getId());
}
// (... ์๋ต ...)
@PreAuthorize("isAuthenticated()")
@GetMapping("/vote/{id}")
public String answerVote(Principal principal, @PathVariable("id") Integer id) {
Answer answer = this.answerService.getAnswer(id);
SiteUser siteUser = this.userService.getUser(principal.getName());
this.answerService.vote(answer, siteUser);
return String.format("redirect:/question/detail/%s#answer_%s",
answer.getQuestion().getId(), answer.getId());
}
}
๋ต๋ณ์ ์์ฑ, ์์ , ์ถ์ฒํ ํ์ ํด๋น ๋ต๋ณ์ผ๋ก ์ด๋ํ๋๋ก ์ต์ปค ํ๊ทธ๋ฅผ ์ถ๊ฐํด ์ฃผ์๋ค.
๋ต๋ณ ์ต์ปค ํ์ธ
ํ๋ฉด์ ๋ค๋ชจ๋ฐ์ค๋ก ํ์ํ ๋ถ๋ถ์ ๋ณด๋ฉด ์์ธ ํ๋ฉด URL์ #answer_8
์ด ์ถ๊ฐ๋์๊ณ , ์คํฌ๋กค์ด ํด๋น ์์น๋ก ์ด๋ํ์์ ์ ์ ์๋ค.