댓글 목록 보기 3가지 전략

coding S's avatar
Mar 20, 2024
댓글 목록 보기 3가지 전략
댓글 목록을 볼 때 순수하게 댓글 목록(댓글 데이터)만 보는게 아니다. Board정보(1), User정보(1), Reply정보(N)가 다 나와야함 -> 화면 확인하면 바로 알 수 있음
notion image


전략 1) ONE관계는 JOIN하고, MANY관계는 조회를 한 번 더 한 후 DTO 담기

notion image
💡
보드와 유저는 ONE 관계 (JOIN 필요) 보드와 댓글은 MANY 관계 (SELECT를 한 번 더 함) -> 담을 DTO 필요

1번 전략 설명

테스트 하려는데.... findByBoardId는 JpaRepository에 존재하지 않기 때문에 (기본 CRUD 아님) ReplyJPARepository에 만들어줘야 함

[ ReplyJPARepository ]

notion image
💡
쿼리 메소드 아니니 @Query 적어주고
 

[ 테스트 하자 ]

notion image
notion image
💡
조회 잘 됨! 이제 여기서 댓글만 JOIN하면 끝!
 

[ 문제 발생! return을 board, reply 이렇게 담을 수 없으니 DTO 필요! ]

[ BoardService ]

notion image
notion image
// 한 번에 캡쳐 할 수 없어서 나눠서 캡쳐함
💡
board랑 replyList 둘 다 return 해줘야 하는데… 둘 다 return 하는 건 불가능! 때문에 둘을 담고 있는 DTO를 생성해서 한 번에 넘겨줘야 한다.
 

[ 쿼리문 조회 ]

notion image
💡
이렇게 하면 밑의 결과가 나오는지 실습으로 확인 필요!
 
notion image
notion image
💡
ONE관계는 join하고, MANY관계는 select 하고.. select가 2번 일어남 !
 

 
1번 전략은 쌤이 좋아하는 방식이다. 현재 댓글은 페이징 안 되어있는 상태. (실제 서비스에서는 댓글도 페이징이 되어야함) join으로 해서 댓글을 페이징하는 쿼리가 엄청 힘들다. join-join-join (3단)으로 페이징하는게 너무 어렵다!! 때문에 select 2번해서 List<Reply> replyList = replyJPARe~~~.findByBoardId(boardId) 를 페이징 하는게 낫다.
 

전략 2) MANY관계를 ‘양방향 매핑’ 하기 → @OneToMany 사용

[ Board테이블 ]

notion image
💡
컬렉션은 필드로 받을 수 없어서 오류

[ @OneToMany 달아주자 ]

notion image
replies는 조회할 때, 담는 용도로만 사용. -> 애가 DB 필드가 되면 안됨! -> @OneToMany 걸기 이 List<Reply> replies는 Board랑 뭘 관계하고 있지? -> 변수명을 적어서 알려줘야 함 즉, FK의 주인이 누구인지 mappedBy로 설정해줘야 함 이때, Entity객체의 변수명을 적어주면 됨!
💡
@Builder는 컬렉션은 허용하지 않기 때문에 List<Reply> replies는 빌더로 만들 수 없다!
💡
@ManyToOne - EAGER 이 디폴트 전략 @OneToMany - LAZY가 디폴트 전략

쌤은 컬렉션 관계는 이렇게 조회를 한 번 더 해서 합치는 걸 선호한다. (1번 방법)

notion image
💡
쌤은 컬렉션 관계는 이렇게 조회를 한 번 더 해서 합친다. (board, replies 두 번 조회)
 

[ why? → 페이징이 가능해서! ]

notion image
💡
이렇게 조인 2번 하는 쿼리를 사용하면 페이징 가능! LAZY 로딩하면… 게시글에 있는 댓글을 죄다 들고 오기 때문에 페이징 못함
 

전략 3) 한방 JOIN

package shop.mtcoding.blog.board; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import java.util.Optional; public interface BoardJPARepository extends JpaRepository<Board, Integer> { @Query("select b from Board b join fetch b.user u where b.id = :id") Optional<Board> findByIdJoinUser(@Param("id") int id); //board에 다 담을 것 @Query("select b from Board b join fetch b.user u join fetch b.replies r where b.id = :id") Optional<Board> findByIdJoinUserAndReplies(@Param("id") int id); }
💡
join 쿼리 작성

[ 테스트 ]

@DataJpaTest public class BoardJPARepositoryTest { @Autowired private BoardJPARepository boardJPARepository; @Autowired private EntityManager em; @Test public void findByIdJoinUserAndReplies_test() { //given int id = 4; //when //옵셔널이라 오류가 나니까 .get 붙여줌. get는 있으니까 가져와! 라는 말 Board boards = boardJPARepository.findByIdJoinUserAndReplies(id).get(); //then }
notion image
 

[ 댓글 목록 보기 3가지 전략 요약 ]

1. One 관계는 join하고, Many 관계는 Lazy Loading 한다. 그리고 DTO에 담는다. select 칠 필요 없음. 자기가 쳐줌. 애를 디폴트로 사용하자!~! (통신 1번 / 12회 조회) 2. One 관계는 join하고, Many 관계를 페이징 (or 복잡한 무언가) 해야 된다면 select를 직접 쿼리를 만들어서 2번 조회하자 (Lazy Loading으로 페이징 안됨!) (통신 2회 / 12회 조회) 3. One 관계와 Many 관계를 한 방에 JOIN 한다. (28회 조회)
💡
현재 프로젝트할 때에는 1번을 써라. 근데 댓글을 페이징 해야한다던지.. 그러면 2번을 사용 → 사실 이걸 써라! 라는 답이 없긴 함.
💡
어떤 전략을 선택해야 할 지 결정할 수 있는 게 중요! (상황에 따라 다르기 때문) 속도, 성능 고려 … 데이터에 따라 고려, 페이징 되었을 때 뭐 이런 것도 고려…해서 공부를 하자^^
 
Share article

codingb