3/29 TIL - 점프투스프링부트 기능 추가

큰모래·2023년 3월 29일
0

점프투스프링부트 - 기능 추가


3-15 (5) 프로필

현재 로그인한 사용자는 자신의 게시글 작성 목록을 확인할 수 있어야 한다.

SiteUser - 추가

  • 기존의 구조는 Question -> SiteUser 참조하는 단방향 관계였음
  • 접속중인 사용자(주체)의 Question 목록을 알아야 하므로 양방향 관계로 변경
	@OneToMany(mappedBy = "author")
    private List<Question> question = new ArrayList<>();

Question - 추가

  • Question 객체의 SiteUser 속성 값을 세팅할 때, 양방향 관계이므로 SiteUser 객체인 authorList<Question>에도 해당 question 값을 추가
  • 양방향 관계는 설정할 때 실수로 까먹고 참조 설정을 안할 수도 있으므로 한번에 해버린다.
	public void setAuthor(SiteUser author) {
        this.author = author;
        author.getQuestion().add(this);
    }

UserController - 추가

  • 접속한 사용자를 알아야하므로 Principal 객체를 매개변수로 받는다.
  • principal.getName 즉, 접속한 사용자의 이름을 통해 DB에 저장된 SiteUser 객체를 반환받는다.
  • SiteUser 객체를 토대로 해당 사용자의 List<Question>을 얻을 수 있다.
  • questionsuser를 모델을 통해 뷰에 넘겨준다.
    @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";
    }

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>

결과물


3-15 (6) 최근 답변과 최근 댓글

질문글이 아닌 최근 답변과 최근 댓글 목록을 보여주는 페이지를 작성해보자

AnswerRepository

  • 최근 답변 목록을 어떻게 불러오는게 좋을지 생각해보다가 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();

}

AnswerService

  • answerRepositoryfindRecentTenAnswer 메서드를 반환한다.
	public List<Answer> getRecentAnswer() {
        return answerRepository.findRecentTenAnswer();
    }

RecentListController

  • answerService.getRecentAnswer()를 통해 최근 10개의 리스트를 불러온다.
  • 이 리스트를 모델에 담아서 뷰로 전달한다.
@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";
    }

}

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>

최근 댓글

최근 댓글 역시 최근 답변과 메커니즘이 똑같으니 생략했다.


결과


3-15 (7) 조회수

게시글 상세페이지를 봤을때 조회수가 올라가고, 목록에서 조회수를 볼 수 있도록 만들어보자.

Question - 추가

 private Long views;

QuestionRepository - 추가

  • @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);

QuestionService - 추가

	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);
    }

QuestionController - 추가

  • 상세페이지 요청이 들어오면 questionServiceupdateView 메서드에 해당 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";
    }

question_list - 추가

  • 리스트에서 조회수를 볼 수 있게 뷰 템플릿을 수정했다.

결과물

profile
큰모래

0개의 댓글