사이먼's 코딩노트

[SpringBoot] 질문/답변 추천 본문

Java/SpringBoot

[SpringBoot] 질문/답변 추천

simonpark817 2024. 5. 16. 15:29

[질문 추천 기능 구현하기]

  • 보통 SNS에서 마음에 드는 게시물이나 컨텐츠에 '좋아요'와 같은 표시를 남기기 마련이다.
  • SBB 프로그램에도 '좋아요' 버튼을 통해 질문이나 답변을 본 다른 사용자들이 반응을 남길 수 있도록 추천 기능을 구현해봅시다.
  • 질문에서 추천 기능을 구현하려면 질문을 추천한 사용자가 DB에 저장될 수 있도록 관련된 속성을 Question 엔티티에 추가해야 한다.
  • Question 엔티티에 추천인을 저장하기 위한 voters라는 이름의 속성을 추가해봅시다.
@ManyToMany
Set<SiteUser> voters = new LinkedHashSet<>();

public void addVoter(SiteUser voter) {
    voters.add(voter);
}
  • @ManyToMany 어노테이션을 사용한 이유는 한 명의 사용자가 여러개의 질문에 대해서 추천을 할 수도 있고, 하나의 질문에 대해서 여러 사용자가 추천을 할 수 있기 때문에 다대다 관계가 성립된다.
  • Set은 List 형식과 동일한데, 차이점이 있다면 중복 금지의 의미가 포함되어있다.
  • addVoter() 메서드는 생성된 voters에 추천인을 저장하는 역할을 담당한다.
  • Question.java 클래스에 위와 같은 코드를 작성하고 DB를 확인하면 question_voters라는 테이블이 새로 생성되는 것을 확인할 수 있다.

 

[question_detail.html 템플릿 수정]

  • 질문 엔티티에 voters 속성을 추가했다면 질문 추천 기능을 구현봅시다.
  • 질문에 좋아요라는 버튼의 위치는 질문 상세 화면에 넣는 것이 적절하기 때문에 아래와 같이 question_detail.html에 코드를 추가 작성해봅시다.
<div class="my-3">
    <a onclick="return confirm('추천하시겠습니까?');" class="btn btn-sm btn-outline-secondary"
       th:href="@{|/question/vote/${question.id}|}">
        좋아요
        <i class="fa-regular fa-thumbs-up"></i>
        <span class="badge rounded-pill bg-success" th:text="${#lists.size(question.voters)}"></span>
    </a>

    <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>

    <a onclick="return confirm('정말로 삭제하시겠습니까?');" th:href="@{|/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>
</div>
  • '좋아요' 버튼의 위치는 수정, 삭제 버튼을 기준으로 가장 왼쪽에 배치하였다.
  • lists.size() 메서드에 question.voters를 인자로 받아 좋아요의 개수도 함께 보이도록 하였다.
  • '좋아요' 버튼을 클릭하면 GET 방식으로 /question/vote/{id} URL을 요청하게 된다.
  • 추가로 onclick="return confirm('추천하시겠습니까?')" 를 속성에 추가하여 '좋아요' 버튼을 클릭했을 때 한 번 더 확인할 수 있도록 alert 알림창이 나오도록 하였다.

 

[Service 수정]

  • 다음은 추천인을 저장할 수 있도록 추천 기능을 아래와 같이 QuestionService.java 클래스에 코드를 추가해봅시다.
public void vote(Question question, SiteUser voter) {
    question.addVoter(voter);

    questionRepository.save(question);
}
  • 위와 같이 로그인한 사용자를 질문 엔티티에 추천인으로 저장하기 위해 vote() 메서드를 추가하였다.

 

[Controller 수정]

  • '좋아요' 버튼을 눌렀을 때 GET 방식으로 호출되는 /question/vote/{id} URL을 처리하기 위해 아래와 같이 QuestionController.java에 코드를 추가해봅시다.
@PreAuthorize("isAuthenticated()")
@GetMapping("/vote/{id}")
public String questionVote(Principal principal, @PathVariable("id") Integer id) {
    Question question = this.questionService.getQuestion(id);
    SiteUser siteUser = this.userService.getUser(principal.getName());

    this.questionService.vote(question, siteUser);

    return "redirect:/question/detail/%s".formatted(id);
}
  • 다른 기능과 마찬가지로 추천 기능도 로그인한 사람만 사용할 수 있도록 @PreAuthorize("isAuthenticated()") 어노테이션을 적용하였다.
  • QuestionService.java에서 작성한 vote() 메서드를 호출하여 사용자와 추천인으로 저장했다.
  • 에러가 없다면 추천인을 저장한 후 질문 상세 페이지로 리다이렉트한다.

 

[답변 추천 기능 구현하기]

  • 질문 추천 기능과 동일하게 이번에는 답변 추천 기능을 구현해봅시다.
  • 답변에서 추천 기능을 구현하려면 답변을 추천한 사용자가 DB에 저장될 수 있도록 관련된 속성을 Answer 엔티티에 추가해야 한다.
  • Answer 엔티티에 추천인을 저장하기 위해 voters라는 이름의 속성을 추가해봅시다.
@ManyToMany
Set<SiteUser> voters = new LinkedHashSet<>();

public void addVoter(SiteUser voter) {
    voters.add(voter);
}
  • @ManyToMany 어노테이션을 사용하여 답변과 사용자 간의 다대다 관계를 가지도록 하였다.
  • 질문 추천 기능 때와 마찬가지로 Set 형식으로 voters라는 리스트 형식의 테이블을 생성하였다.
  • addVoter() 메서드는 생성된 voters에 추천인을 저장하는 역할을 담당한다.
  • Answer.java 클래스에 위와같은 코드를 작성하고 DB를 확인하면 answer_voters라는 테이블이 새로 생성되는 것을 확인할 수 있다.

 

[question_detail.html 템플릿 수정]

  • 답변 엔티티에 voters 속성을 추가했다면 답변 추천 기능을 구현해봅시다.
  • 답변에 좋아요라는 버튼의 위치는 질문 상세 화면에서 답변을 보여주는 곳에 넣는 것이 적절하기 때문에 아래와 같이 question_detail.html에 코드를 추가 작성해봅시다.
<div class="my-3">
    <a onclick="return confirm('추천하시겠습니까?');" class="btn btn-sm btn-outline-secondary"
       th:href="@{|/answer/vote/${answer.id}|}">
        좋아요
        <i class="fa-regular fa-thumbs-up"></i>
        <span class="badge rounded-pill bg-success" th:text="${#lists.size(answer.voters)}"></span>
    </a>

    <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 onclick="return confirm('정말로 삭제하시겠습니까?')" th:href="@{|/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>
  • '좋아요' 버튼의 위치는 수정, 삭제 버튼을 기준으로 가장 왼쪽에 배치하였고, #lists.size(answer.voters)를 좋아요의 개수도 함께 보이도록 하였다.
  • '좋아요' 버튼을 클릭하면 GET 방식으로 /answer/vote/{id} URL을 요청하게 된다.
  • 추가로 onclick="return confirm('추천하시겠습니까?')" 를 속성에 추가하여 '좋아요' 버튼을 클릭했을 때 한 번 더 확인할 수 있도록 alert 알림창이 나오도록 하였다. 

 

[Service 수정]

  • 답변을 추천한 사람을 저장하기 위해 아래와 같이 AnswerService.java 클래스에 코드를 추가해봅시다.
public void vote(Answer answer, SiteUser voter) {
    answer.addVoter(voter);

    answerRepository.save(answer);
}
  • 위와 같이 로그인한 사용자를 답변 엔티티에 추천인으로 저장하기 위해 vote() 메서드를 추가하였다.

 

[Controller 수정]

  • '좋아요' 버튼을 눌렀을 때 GET 방식으로 호출되는 /answer/vote/{id} URL을 처리하기 위해 아래와 같이 AnswerController.java에 코드를 추가해봅시다.
@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 "redirect:/question/detail/%d#answer_%d".formatted(answer.getQuestion().getId(), id);
}
  • 다른 기능과 마찬가지로 로그인한 사람만 사용할 수 있도록 @PreAuthorize 어노테이션을 적용하고 AnswerService.java에서 작성한 vote() 메서드를 호출하여 사용자와 추천인으로 저장하였다.
  • 에러가 없다면 추천인을 저장한 후 질문 상세 페이지로 리다이렉트한다.

 

  • 위에서 설명한 모든 코드를 작성하고 로컬 서버를 다시 실행하면 localhost:8090/question/detail 페이지에서 각 질문과 답변에 대해서 '좋아요' 버튼이 생성된 것과 실제로 버튼을 눌렀을 때 해당 기능이 동작하여 숫자가 카운팅되는 것을 확인할 수 있다.

추천(좋아요) 기능 추가

 

추천 기능 실행 전 마지막 확인

반응형