강의

멘토링

로드맵

인프런 커뮤니티 질문&답변

pink102557님의 프로필 이미지
pink102557

작성한 질문수

컬렉션 조회 시 영속성 컨텍스트

작성

·

42

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에서의 엔티티 관리와 페치 전략이 복합적으로 작용하는 과정에서 발생할 수 있는 일입니다.

  1. 영속성 컨텍스트와 fetch join: getRecommendedPosts3 메서드처럼 fetch join을 사용하는 경우, 연관된 엔티티들이 한 번의 쿼리를 통해 영속성 컨텍스트에 저장됩니다. 따라서 이미 영속성 컨텍스트에 로딩된 엔티티에 대해서는 추가적인 쿼리가 나가지 않습니다.

  2. EntityGraph 및 findAllByMemePostId 메서드: getRecommendedPosts2 메서드에서는 @EntityGraph와 함께 findAllByMemePostId 메서드를 사용하지만 이것은 실제 fetch join 쿼리와는 다르게 JPA에서 일반적으로 이력되지 않은 결합을 가져오는 것을 의미할 수 있습니다. EntityGraphJPQLfetch join처럼 한 번의 쿼리로 여러 엔티티를 가져오는 것이 아니라, Lazy Loading될 수 있는 환경을 제공합니다. 즉, memePost.getTagNames() 호출 시점에 태그가 로드되지 않은 상태라면 추가 쿼리가 발생할 수 있습니다.

  3. Lazy Loading: MemePostmemePostTagsTag 엔티티가 lazy 로딩 방식으로 초기화된다면 필요한 시점에만 로딩될 수 있습니다. 이는 findByIdWithTags처럼 fetch join을 통해 eager로 로딩하지 않는 한, 기본적으로 lazy로 동작할 수 있습니다.

따라서, 문제를 해결하기 위해서는 fetch join을 이용하여 한 번의 쿼리로 데이터를 확보하거나, EntityGraph 사용 시 특정 설정을 확인하여 lazy 로딩이 일어나는 시점과 데이터를 참조하는 시점을 맞추어야 합니다.

추가적인 링크와 코드 예제를 참조해 주세요:
- Spring Data JPA - Reference Documentation

질문 남겨주셔서 감사합니다. 더 궁금한 점이 있다면 언제든지 문의하세요!

pink102557님의 프로필 이미지
pink102557

작성한 질문수

질문하기