(18) 기능구현 - 앵커
앵커 구현하기
답글을 작성하거나 수정한 후에 항상 페이지 상단으로 스크롤이 이동된다.
때문에 작성한 답변을 확인하려면 다시 스크롤을 내려서 확인해야 하기 때문에 매우 번거롭다.
이 문제는 답변을 추천한 경우에도 동일하게 발생한다.
HTML의 앵커 태그를 사용하여 이 문제를 해결해보자.
시작하기 전에
개요 : 질문과 답변을 할 수 있는 게시판 서비스를 스프링부트를 통해 만들어 본다.
학습사이트 : 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}">
<a th:id="|answer_${answer.id}|"></a> <!-- 앵커태그 -->
<div class="card-body">
(... 생략 ...)
</html>
답변 반복 부분 상단에 앵커 태그를 추가
답변 redirect
- AnswerService.java 수정
// 경로 : 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;
}
(... 생략 ...)
}
컨트롤러에서 답변이 등록된 위치로 이동시켜주기 위해 답변 객체를 void에서 변경
- 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 {
// 변수 지정
private final QuestionService questionService;
private final AnswerService answerService;
private final UserService userService;
// 답변 (POST) 처리
@PreAuthorize("isAuthenticated()")
@PostMapping("/create/{id}") // post요청만 받아들일 경우에 사용하는 에너테이션. (value=) 생략가능
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());
}
(... 생략 ...)
// 답변 수정 (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_%s",
answer.getQuestion().getId(),
answer.getId());
}
(... 생략 ...)
// 추천 URL 매핑 (GET)
@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());
if(answer.getVoter().contains(siteUser) == true) {
this.answerService.votedel(answer, siteUser);
}
else {
this.answerService.vote(answer, siteUser);
}
// 답변 추천 후 앵커로 돌아감
return String.format("redirect:/question/detail/%s#answer_%s",
answer.getQuestion().getId(),
answer.getId());
}
}
생성, 수정, 추천 후 각 앵커로 return해준다.
SBB테스트
주소창에 앵커가 표시되고, 자동으로 스크롤되어 이동된다.
자동 스크롤이다보니 Ajax를 통해 구현하는게 더 깔끔할거 같다.
Leave a comment