작성
·
108
·
수정됨
0
[질문 템플릿]
1. 강의 내용과 관련된 질문인가요? - 예
2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? - 예
3. 질문 잘하기 메뉴얼을 읽어보셨나요? - 예
[질문 내용]
안녕하세요! 마치 학교 선배가 옆에서 가르쳐주는 것과 같은 친근한 스타일의 강의 덕분에 백엔드 개발에 좀 더 가까워지는 느낌을 받고 있습니다!
다름이 아니라, 복잡한 구조를 가진 단일 엔티티 조회(DTO 프로젝션) API 개발 관련 질문이 있습니다!
class MemberDto {
private Long id;
private String username;
private String imageUrl;
// 사용자가 작성한 총 게시물 수
private Long postCount;
}
// Post와 Hashtag는 다대다 연관 관계 - PostHashtag 중간 엔티티(중간 테이블) 추가 정의
class HashtagDto {
private Long id;
private String name;
}
// Post와 Category는 다대다 연관 관계 - PostCategory 중간 엔티티(중간 테이블) 추가 정의
class CategoryDto {
private Long id;
private String name;
}
// API로 조회를 제공하려는 대상 - 사용자(Member)가 작성할 수 있는 게시물.
class PostDto {
private Long id;
private MemberDto member;
private String title;
private String body;
private List<HashtagDto> hashtags;
private List<CategoryDto> hashtags;
}
여러 개(해시태그, 카테고리)의 컬렉션으로 데이터(도메인/엔티티)가 구성돼있을 때,
PostDto findById(Long id)
위와 같이 ID에 대한 게시물의 상세 정보를 조회하는 리포지토리 계층(PostRepository 같은 클래스)에서의 쿼리 메서드는 강의에서 말씀주신 것을 따르면, (컬렉션에 대한 페치 조인은 1번으로 제한되기에)
Post 및 Post와 xToOne 연관 관계인 Member를 페치 조인으로 조회하는 쿼리
Post와 xToMany 연관 관계인 모든 Hashtag를 Post.id로 조회하는 쿼리
Post와 xToMany 연관 관계인 모든 Category를 Post.id로 조회하는 쿼리
n개의 컬렉션이 있을 때 위와 같은 식으로 Post, Member를 제외한 n개의 컬렉션에 대한 n번의 쿼리를 실행해서, 마지막에 Dto로 조립하는 게 최선일까요?
한 방 쿼리(?)를 위해 DTO로 프로젝션하는 방식을 찾아보니, QueryDSL의 transform, groupBy, list 같은 것을 써서 구현한 개발한 블로그가 있긴 하던데, 막상 코드를 실행하니 안 되는 경우가 많더라고요..!
실무에서도 위와 같이 여러 복잡한 xToMany 연관 관계 필드가 포함된 DTO를 조회하는 경우가 많을 것 같은데, 주로 어떤식으로 쿼리가 짜여지는지 궁금합니다!
답변 2
0
안녕하세요. 손원희님, 공식 서포터즈 y2gcoder입니다.
제일 중요한 건 먼저 DB에서의 조인 쿼리나 네이티브 쿼리를 사용해 한 방으로 조회하는 것과, 각각 조회해서 조립하는 것 사이에서의 트레이드 오프를 따지는 것이 좋습니다. 성능, 재사용 등 많은 부분을 고려해야 한다고 생각합니다.
저는 보통 상세는 루트 엔티티에 대해 단건으로 조회하는 것이기 때문에 보통 xToOne 은 페치 조인이나 엔티티 그래프를 사용해서 같이 조회하고, 나머지 컬렉션들은 페치 사이즈를 통해 지연 로딩으로 조회해서 가공하는 식으로 먼저 만들고, 성능 테스트를 통해 최적화하는 방식으로 진행하는 것 같습니다.
감사합니다.
0
안녕하세요, 인프런 AI 인턴입니다. 손원희님.
복잡한 구조의 DTO를 한 번에 조회하는 것은 많은 JPA 개발자들이 직면하는 공통적인 과제입니다. 일대다 관계가 여러 개 포함된 DTO를 효율적으로 조회하기 위해서는 몇 가지 접근 방식을 고려할 수 있습니다. 자세한 설명과 유사한 질문에 대한 답변을 통해 설명드리겠습니다.
Fetch Join 사용: 일대다 관계를 포함하는 경우 페치 조인(fetch join)을 사용하여 해당 관계의 데이터를 한번에 가져오는 방법이 유용할 수 있습니다. 특히, 페이징이 필요 없는 경우에는 크게 문제가 되지 않습니다. 하지만, 데이터 중복(row 수 증가) 문제가 발생할 수 있으므로, distinct
를 사용하여 중복을 제거할 수 있습니다.
DTO로 변환하는 방식: DTO를 사용하여 필요한 데이터만 선택적으로 가져오는 것도 고려할 수 있습니다. 이 경우에는 주로 JPQL이나 QueryDSL을 활용합니다. QueryDSL은 복잡한 쿼리를 작성할 때 유연성을 제공하여 특히 유용합니다.
QueryDSL의 transform, groupBy 사용: QueryDSL에서는 복잡한 관계를 다룰 때 특히 유용하게 활용할 수 있는 기능들이 있습니다. 크게는 transform
메서드와 groupBy
를 조합하여 복잡한 쿼리를 일대일 매핑할 수 있습니다.
참고 링크:
- Querydsl을 활용한 복잡한 쿼리 구현 방법에서는 다양한 방식과 접근법이 설명되어 있습니다. 특히 쿼리를 그룹핑하여 성능을 최적화하는 방법에 대해 구체적인 예시와 함께 설명합니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.
답변 감사합니다! 혹시 관련해서 하나만 더 질문드려도 될까요?
컬렉션에 대해 배치 지연 로딩으로 조회할 때,
Post - Hashtag 처럼 many-to-many 연관 관계일 때 이를 중간 PostHashtag라는 테이블/엔티티로 해소한 경우,
id에 대한 Post 상세 조회 시, 해당 Post와 연관 관계인 모든 Hashtag 목록을 PostHashtag 통해 최대한 잘 가져오려면
게시물 자신 및 to-one 연관 관계인 엔티티에 대해선 페치 조인으로 함께 조회하는 한 방 쿼리 실행
to-many 연관 관계인 게시물의 해시태그 목록 등은 PostHashtag와 Hashtag를 페치 조인으로 함께 조회하는 쿼리 실행 (즉, to-many 연관 관계인 엔티티 개수만큼 쿼리 실행)
위 방법이 최선일까요?
처음엔 (to-one 엔티티들만 페치 조인해서 가져온) Post를 통해 Hashtag들을 배치 적용 후 지연 로딩하려고 했는데, 사실 Post와 직접 연관있는 대상은 결국 PostHashtag까지여서, PostHashtag를 통해 한 번 더 깊게 Hashtag를 조회하는 쿼리가 추가로 실행되어버리는 것을 보고 고민이 되더라고요 ㅠㅠ