현재 로그인한 사용자는 자신의 게시글 작성 목록을 확인할 수 있어야 한다.
Question
-> SiteUser
참조하는 단방향 관계였음Question
목록을 알아야 하므로 양방향 관계로 변경 @OneToMany(mappedBy = "author")
private List<Question> question = new ArrayList<>();
Question
객체의 SiteUser
속성 값을 세팅할 때, 양방향 관계이므로 SiteUser
객체인 author
의 List<Question>
에도 해당 question
값을 추가 public void setAuthor(SiteUser author) {
this.author = author;
author.getQuestion().add(this);
}
Principal
객체를 매개변수로 받는다.principal.getName
즉, 접속한 사용자의 이름을 통해 DB에 저장된 SiteUser
객체를 반환받는다.SiteUser
객체를 토대로 해당 사용자의 List<Question>
을 얻을 수 있다.questions
와 user
를 모델을 통해 뷰에 넘겨준다. @GetMapping("/question")
public String userQuestionList(Principal principal, Model model) {
SiteUser user = userService.getUser(principal.getName());
List<Question> questions = user.getQuestion();
model.addAttribute("questions", questions);
model.addAttribute("user", user);
return "/user/mypage_question";
}
<!DOCTYPE html>
<html layout:decorate="~{layout}" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<div layout:fragment="content" class="container my-3">
<h5 class="border-bottom pb-2" th:text="${user.username}"></h5>
<ul class="nav nav-tabs">
<li class="nav-item">
<a class="nav-link " href="/user/profile">기본정보</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="/user/question">게시</a>
</li>
<li class="nav-item">
<a class="nav-link " href="/user/answer">답변</a>
</li>
<li class="nav-item">
<a class="nav-link " href="/user/comment">댓글</a>
</li>
</ul>
<table class="table">
<thead class="table-dark">
<tr class="text-center">
<th>분류</th>
<th style="width: 50%">제목</th>
<th>작성자</th>
<th>작성일지</th>
</tr>
</thead>
<tbody>
<tr class="text-center" th:each="question, loop : ${questions}" >
<td th:text="${question.category}"></td>
<td class="text-start">
<a th:href="@{|/question/detail/${question.id}|}" th:text="${question.subject}"></a>
<span class="text-danger small ms-2" th:if="${#lists.size(question.answerList)>0}"
th:text="${#lists.size(question.answerList)}"></span>
</td>
<td>
<span th:if="${question.author != null}" th:text="${question.author.username}"></span>
</td>
<td th:text="${#temporals.format(question.createData, 'yyyy-MM-dd HH:mm')}"></td>
</tr>
</tbody>
</table>
</div>
</html>
질문글이 아닌 최근 답변과 최근 댓글 목록을 보여주는 페이지를 작성해보자
JPQL
을 선택했다.findRecentTenAnswer()
에는 최근 10개의 답변 목록이 저장된다.
public interface AnswerRepository extends JpaRepository<Answer, Long> {
Page<Answer> findAllByQuestion(Question question, Pageable pageable);
@Query(value = "SELECT an FROM Answer an ORDER BY an.createDate DESC LIMIT 10")
List<Answer> findRecentTenAnswer();
}
answerRepository
의 findRecentTenAnswer
메서드를 반환한다. public List<Answer> getRecentAnswer() {
return answerRepository.findRecentTenAnswer();
}
@Controller
@RequestMapping("/recent_list")
@RequiredArgsConstructor
public class RecentListController {
private final AnswerService answerService;
@GetMapping("/answer")
public String recentAnswerList(Model model) {
List<Answer> recentAnswers = answerService.getRecentAnswer();
model.addAttribute("recentAnswers", recentAnswers);
return "/recentList/recentList_answer";
}
}
<!DOCTYPE html>
<html layout:decorate="~{layout}" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<div layout:fragment="content" class="container my-3">
<h5 class="border-bottom pb-2 mb-3">최근이력</h5>
<ul class="nav nav-tabs mb-3" id="myTab" role="tablist">
<li class="nav-item" role="presentation">
<a class="nav-link active" id="answer-tab" data-toggle="tab" th:href="@{/recent_list/answer}" role="tab" aria-controls="answer"
aria-selected="true">최근답변 (10)</a>
</li>
<li class="nav-item" role="presentation">
<a class="nav-link" id="comment-tab" data-toggle="tab" th:href="@{/recent_list/comment}" role="tab" aria-controls="comment"
aria-selected="true">최근댓글 (10)</a>
</li>
</ul>
<table class="table">
<thead class="table-dark">
<tr class="text-center">
<th>분류</th>
<th>작성자</th>
<th style="width: 50%">글내용</th>
<th>작성일지</th>
</tr>
</thead>
<tbody>
<tr class="text-center" th:each="answer : ${recentAnswers}" >
<td th:text="${answer.question.category}"></td>
<td>
<span th:if="${answer.question.author != null}" th:text="${answer.author.username}"></span>
</td>
<td class="text-start">
<a th:href="@{|/question/detail/${answer.question.id}|}" th:text="${answer.content}"></a>
</td>
<td th:text="${#temporals.format(answer.createDate, 'yyyy-MM-dd HH:mm')}"></td>
</tr>
</tbody>
</table>
</div>
</html>
최근 댓글 역시 최근 답변과 메커니즘이 똑같으니 생략했다.
게시글 상세페이지를 봤을때 조회수가 올라가고, 목록에서 조회수를 볼 수 있도록 만들어보자.
private Long views;
@Query
를 통해서 question
엔티티의 조회수를 올리는 쿼리문을 작성했다.@Query
에서 조회가 아닌 삽입, 수정, 삭제 메서드는 @Modifying
을 붙여아한다.@Modifying
을 안붙이면 삽입,수정,삭제 쿼리는 DB로 쿼리를 직접 날려 영속성 컨텍스트와 DB의 엔티티 데이터 차이가 발생한다.@Transactional
을 통해 하나의 트랜잭션 내에서 실행되도록 보장하고, 데이터의 일관성을 유지한다. @Modifying
@Query(value = "UPDATE Question q SET q.views=q.views + 1 where q.id = :id")
@Transactional
Integer updateView(@Param("id") Long id);
public void create(String subject, String content, Category category, SiteUser author) {
Question question = new Question();
question.setSubject(subject);
question.setContent(content);
question.setCreateData(LocalDateTime.now());
question.setAuthor(author);
question.setCategory(category);
question.setViews(0L); // 추가한 부분
questionRepository.save(question);
}
public void updateView(Long id) {
questionRepository.updateView(id);
}
questionService
의 updateView
메서드에 해당 question
의 id를 매개변수로 넣어 조회수를 1 증가시킨다. @GetMapping("/detail/{id}")
public String detail(@PathVariable Long id, Model model, AnswerForm answerForm, CommentForm commentForm,
@RequestParam(value = "page", defaultValue = "0") int page) {
Question question = questionService.getQuestion(id);
Page<Answer> paging = answerService.getAnswerList(page, question);
questionService.updateView(id);
model.addAttribute("paging", paging);
model.addAttribute("question", question);
return "question/question_detail";
}