[v3] SpringBoot 블로그 만들기 -3

댓글 조회 기능 완성
HootJem's avatar
Sep 05, 2024
[v3] SpringBoot 블로그 만들기 -3
SELECT bt.id, bt.title, bt.content, ut.username, rt.comment, rut.username FROM board_tb bt INNER JOIN user_tb ut ON bt.user_id = ut.id LEFT OUTER JOIN reply_tb rt on bt.id = rt.board_id LEFT OUTER JOIN user_tb rut on rut.id = rt.user_id where bt.id = 5;
notion image
 
쿼리문은 작성 완료 함.
notion image
데이터를 넘길 DTO 를 만들 때
int id;
String title;
String content;
List<reply> replies;
이렇게 한 게시글에 속한 댓글을 컬렉션으로 주는게 좋다.
 
이것을 JPQL 쿼리로 작성하면 이러하다
@Query("select b from Board b join fetch b.user left join fetch b.replies r left join fetch r.user where b.id=:id") Optional<Board> mFindByIdWithReply(@Param("id")int id);
 
notion image
  • 쿼리 테스트
@Test public void mFindByIdWithReply_test(){ Board board = boardRepository.mFindByIdWithReply(5).get(); System.out.println(board.getReplies().get(0).getComment()); }
필요한 데이터만 전달할 때 stream 같은 걸 사용해 수동으로 매핑할 필요 없이, 퍼시스턴스 컨텍스트가 자동으로 매핑해준다.
즉, board 엔티티는 다음과 같은 구조를 가지고 있다:
  • board
    • user
    • reply
이처럼 데이터가 자동으로 매핑되는 이유는 엔티티 클래스에서 양방향 매핑이 설정되어 있기 때문이다. 이를 통해 연관된 데이터들을 직관적으로 불러올 수 있고, 따로 데이터를 변환하거나 매핑하는 코드가 필요하지 않다.
notion image
이대로 데이터를 뿌리면 replies 를 꺼내기 위해 스크립트를 많이 짜게됨.
notion image
 
데이터 매핑을 위한 DTO 생성
notion image
댓글도 수정, 삭제 할 것이기 때문에 isOwner 추가하여 작성자 일치 여부 확인
코드
package org.example.springv3.board; import lombok.Data; import org.example.springv3.reply.Reply; import org.example.springv3.user.User; import java.util.ArrayList; import java.util.List; public class BoardResponse { @Data public static class DetailDTO { private Integer id; private String title; private String content; private Boolean isOwner; private String username; // 댓글들 private List<ReplyDTO> replies = new ArrayList<>(); //엔티티 말고 DTO 를 넣어야함. 엔티티 넣으면 레이지로딩? 나옴 public DetailDTO(Board board, User sessionUser) { this.id = board.getId(); this.title = board.getTitle(); this.content = board.getContent(); this.isOwner = false; if (sessionUser != null) { if (board.getUser().getId() == sessionUser.getId()) { isOwner = true; // 권한체크 } } this.username = board.getUser().getUsername(); for (Reply reply : board.getReplies()) { replies.add(new ReplyDTO(reply, sessionUser)); } } @Data class ReplyDTO { private Integer id; private String comment; private String username; private Boolean isOwner; public ReplyDTO(Reply reply, User sessionUser) { this.id = reply.getId(); this.comment = reply.getComment(); this.username = reply.getUser().getUsername(); this.isOwner = false; if (sessionUser != null) { if (reply.getUser().getId() == sessionUser.getId()) { isOwner = true; // 권한체크 } } } } } }
{ "id": 5, "title": "제목5", "content": "내용5", "isOwner": false, "username": "cos", "replies": [ { "id": 1, "comment": "댓글1", "username": "ssar", "isOwner": false }, { "id": 2, "comment": "댓글2", "username": "ssar", "isOwner": false }, { "id": 3, "comment": "댓글3", "username": "cos", "isOwner": false } ] }
데이터 결과는 이러하다.
 
이제 템플릿 엔진만 수정하면 완료.
전체 코드
{{>layout/header}} <div class="container p-5"> <!-- 수정삭제버튼 --> {{#model.isOwner}} <div class="d-flex justify-content-end"> <a href="/api/board/{{model.id}}/update-form" class="btn btn-warning me-1">수정</a> <form action="/api/board/{{model.id}}/delete" method="post"> <button class="btn btn-danger">삭제</button> </form> </div> {{/model.isOwner}} <div class="d-flex justify-content-end"> <b>작성자</b> : {{model.username}} </div> <!-- 게시글내용 --> <div> <h2><b>{{model.title}}</b></h2> <hr/> <div class="m-4 p-2"> {{{model.content}}} </div> </div> </div> <!-- 댓글 --> <div class="card mt-3"> <!-- 댓글등록 --> <div class="card-body"> <form action="/reply/save" method="post"> <input type="hidden" name="boardId" value=""> <textarea class="form-control" rows="2" name="comment"></textarea> <div class="d-flex justify-content-end"> <button type="submit" class="btn btn-outline-primary mt-1">댓글등록</button> </div> </form> </div> <!-- 댓글목록 --> <div class="card-footer"> <b>댓글리스트</b> </div> <div class="list-group"> {{#model.replies}} <!-- 댓글아이템 --> <div class="list-group-item d-flex justify-content-between align-items-center"> <div class="d-flex"> <div class="px-1 me-1 bg-primary text-white rounded">{{username}}</div> <div>{{comment}}</div> </div> {{#isOwner}} <form action="#" method="post"> <button class="btn">🗑</button> </form> {{/isOwner}} </div> {{/model.replies}} </div> </div> {{>layout/footer}}
 
notion image
notion image

 
결과
notion image
ssar 로 로그인 하였기 때문에 본인 댓글만 휴지통 이모지가 보인다.
다음 포스팅에서는 AJAX통신을 활용하여 댓글 수정 삭제를 구현할 예정이다.
 
사용된 DTO 데이터 관리 방법
 
SpringBoot 블로그 만들기 - v3 시리즈 1. https://inblog.ai/hj/v3-시작-27809 (개발환경 설정 및 post 맨 이용한 api 테스트) 2. https://inblog.ai/hj/v3-springboot-블로그-만들기-2-28708 (댓글 엔티티 생성 및 양방향 매핑) 3. https://inblog.ai/hj/v3-springboot-블로그-만들기-3-28793 (댓글 조회하기 완료)
 
Share article

[HootJem] 개발 기록 블로그