인프런 커뮤니티 질문&답변
컬렉션 조회 시 영속성 컨텍스트
작성
·
43
0
게시글 프로젝트를 하다가 컬렉션 조회 관련 영속성 컨텍스트가 이해가 가지 않는 부분이 있어서 질문을 드리게 되었습니다.
getRecommedPosts2메서드에서
findAllByMemePostId로 태그 정보를 미리 가져오면 영속성 컨텍스트에 MemePostTag와 Tag가 같이 불려와지니까
이후에 memePost.getTagNames()를 했을때 따로 쿼리가 나가지 않을 거라고 생각했는데, 별도로 또 쿼리가 나가는 것을 확인했습니다.
그래서 getRecommedPosts3메서드에서 findByIdWithTags와 같이 MemePostTag와 Tag를 같이 fetch join하면 따로 쿼리가 나가지 않더라구요
혹시 이유가 무엇인가요? 제가 어떤 개념을 잘 모르는 것 같은데 뭘 모르는 것인지 모르겠어요. 저는 getTagNames()를 하면 memePostTags 컬렉션이 필요할때 id로 영속성 컨텍스트에 있는지 조회한다고 생각해서 필요한 엔티티들을 미리 불러와 놓으면 따로 쿼리가 나가지 않을 것이라고 생각했습니다.
public List<MemePostSummaryResponse> getRecommendedPosts2(Long memePostId, int size) {
MemePost memePost = getMemePostById(memePostId);
//fetch join으로 memePost의 태그 정보 미리 가져오기
memePostTagRepository.findAllByMemePostId(memePost.getId());
//tag 이름으로 관련있는 포스트 찾기
List<MemePost> recommendedPosts = memePostRepository.findRelatedPostsByTagNames(memePost.getTagNames(), memePostId, PageRequest.of(0, size));
memePostTagRepository.findTagsByMemePostIdIn(getPostIds(recommendedPosts));
return recommendedPosts.stream()
.map(mp -> new MemePostSummaryResponse(mp, false, mp.getTagNames()))
.toList();
}
public interface MemePostTagRepository extends JpaRepository<MemePostTag, Long> {
@EntityGraph(attributePaths = {"tag"})
List<MemePostTag> findAllByMemePostId(Long postId);
}public class MemePost {
public List<String> getTagNames() {
return memePostTags.stream()
.map(mpt -> mpt.getTag().getName())
.toList();
}
}public List<MemePostSummaryResponse> getRecommendedPosts3(Long memePostId, int size) {
MemePost memePost = memePostRepository.findByIdWithTags(memePostId)
.orElseThrow(() -> new FindMyMemeException(ErrorCode.NOT_FOUND_MEME_POST));
//fetch join으로 memePost의 태그 정보 미리 가져오기
//tag 이름으로 관련있는 포스트 찾기
List<MemePost> recommendedPosts = memePostRepository.findRelatedPostsByTagNames(memePost.getTagNames(), memePostId, PageRequest.of(0, size));
memePostRepository.findAllWithTagsInPostIds(getPostIds(recommendedPosts));
return recommendedPosts.stream()
.map(mp -> new MemePostSummaryResponse(mp, false, mp.getTagNames()))
.toList();
}@Query("select distinct m from MemePost m " +
"left join fetch m.memePostTags mpt " +
"left join fetch mpt.tag t " +
"where m.id = :id and m.deletedAt is null")
Optional<MemePost> findByIdWithTags(@Param("id") Long id);답변 1
0
안녕하세요, 인프런 AI 인턴입니다.
안채연님, 질문해주신 영속성 컨텍스트 관련 문제는 JPA에서의 엔티티 관리와 페치 전략이 복합적으로 작용하는 과정에서 발생할 수 있는 일입니다.
영속성 컨텍스트와 fetch join:
getRecommendedPosts3메서드처럼fetch join을 사용하는 경우, 연관된 엔티티들이 한 번의 쿼리를 통해 영속성 컨텍스트에 저장됩니다. 따라서 이미 영속성 컨텍스트에 로딩된 엔티티에 대해서는 추가적인 쿼리가 나가지 않습니다.EntityGraph 및
findAllByMemePostId메서드:getRecommendedPosts2메서드에서는@EntityGraph와 함께findAllByMemePostId메서드를 사용하지만 이것은 실제fetch join쿼리와는 다르게 JPA에서 일반적으로 이력되지 않은 결합을 가져오는 것을 의미할 수 있습니다.EntityGraph는JPQL의fetch join처럼 한 번의 쿼리로 여러 엔티티를 가져오는 것이 아니라,Lazy Loading될 수 있는 환경을 제공합니다. 즉,memePost.getTagNames()호출 시점에 태그가 로드되지 않은 상태라면 추가 쿼리가 발생할 수 있습니다.Lazy Loading:
MemePost의memePostTags나Tag엔티티가lazy로딩 방식으로 초기화된다면 필요한 시점에만 로딩될 수 있습니다. 이는findByIdWithTags처럼fetch join을 통해eager로 로딩하지 않는 한, 기본적으로lazy로 동작할 수 있습니다.
따라서, 문제를 해결하기 위해서는 fetch join을 이용하여 한 번의 쿼리로 데이터를 확보하거나, EntityGraph 사용 시 특정 설정을 확인하여 lazy 로딩이 일어나는 시점과 데이터를 참조하는 시점을 맞추어야 합니다.
추가적인 링크와 코드 예제를 참조해 주세요:
- Spring Data JPA - Reference Documentation
질문 남겨주셔서 감사합니다. 더 궁금한 점이 있다면 언제든지 문의하세요!




