Fetch Join을 사용하는 것이 항상 최적의 방법이라고 생각했다. 하지만 실제로는 불필요한 조인으로 인해 같은 데이터를 중복 조회하는 문제가 발생할 수 있었다.Fetch Join을 사용하여 좋아요 엔터티까지 함께 불러올 수도 있었지만, 이미 1:N 관계를 하나 Fetch Join한 상태였기 때문에 추가적인 조회를 수행하는 방식으로 결정했다. // PostServiceImpl.class
@Override
@Transactional
public PostGetDto getPost(Long postId, Long memberId, boolean flag) {
Post post = postRepository.findPostWithField(postId)
.orElseThrow(() -> new NotFoundException("Post not found"));
// 조회수 추가
if (flag) post.updateViewCount();
// 로그인 하지 않은 사용자의 경우 게시글만 반환
if (memberId.equals(GUEST_USER)) {
return PostGetDto.toDto(post, new PostUserStatusDto());
}
// 좋아요 표시 여부 및 작성자 여부
PostUserStatusDto postUserStatusDto = getPostAuthInfo(memberId, postId,
post.getMember().getId());
return PostGetDto.toDto(post, postUserStatusDto);
}
private PostUserStatusDto getPostAuthInfo(Long memberId, Long postId,
Long postOwnerId) {
boolean isLiked = likeRepository.findByPostIdAndMemberId(postId, memberId)
.isPresent();
boolean isOwner = postOwnerId.equals(memberId);
return new PostUserStatusDto(isLiked, isOwner);
}
// PostRepository.class
@Query("""
select p
from Post p
join fetch p.member m
join fetch p.recruitmentInfo r
join fetch r.fieldList f
left join fetch p.image
where p.id = :id
""")
Optional<Post> findPostWithField(@Param("id") Long id);
// LikeRepository.class
@Query("select l from Like l join fetch l.post p join fetch l.member m where p.id = :postId and m.id = :memberId")
Optional<Like> findByPostIdAndMemberId(@Param("postId") Long postId,
@Param("memberId") Long memberId);
select
p1_0.post_id,
p1_0.category,
p1_0.content,
p1_0.created_at,
p1_0.created_by,
i1_0.image_id,
i1_0.full_path,
i1_0.store_file_name,
i1_0.upload_file_name,
p1_0.like_count,
m1_0.member_id,
m1_0.content,
m1_0.email,
m1_0.login_id,
m1_0.major,
m1_0.nickname,
m1_0.password,
m1_0.username,
p1_0.modified_at,
p1_0.modified_by,
ri1_0.recruitment_info_id,
ri1_0.end_date,
fl1_0.recruitment_info_id,
fl1_0.id,
fl1_0.current_recruitment,
fl1_0.field_category,
fl1_0.total_recruitment,
ri1_0.start_date,
ri1_0.status,
p1_0.title,
p1_0.view_count
from
post p1_0
join
member m1_0
on m1_0.member_id=p1_0.member_id
join
recruitment_info ri1_0
on p1_0.post_id=ri1_0.post_id
join
field fl1_0
on ri1_0.recruitment_info_id=fl1_0.recruitment_info_id
left join
image i1_0
on p1_0.post_id=i1_0.post_id
where
p1_0.post_id=?
2025-03-18T00:26:34.280+09:00 DEBUG 60969 --- [nio-8080-exec-5] org.hibernate.SQL :
select
l1_0.id,
m1_0.member_id,
m1_0.content,
m1_0.email,
m1_0.login_id,
m1_0.major,
m1_0.nickname,
m1_0.password,
m1_0.username,
p1_0.post_id,
p1_0.category,
p1_0.content,
p1_0.created_at,
p1_0.created_by,
p1_0.like_count,
p1_0.member_id,
p1_0.modified_at,
p1_0.modified_by,
p1_0.title,
p1_0.view_count
from
likes l1_0
join
post p1_0
on p1_0.post_id=l1_0.post_id
join
member m1_0
on m1_0.member_id=l1_0.member_id
where
p1_0.post_id=?
and m1_0.member_id=?
@Query("select l from Like l where l.post.id = :postId and l.member.id = :memberId")
Optional<Like> findByPostIdAndMemberId(@Param("postId") Long postId,
@Param("memberId") Long memberId);
영속성 컨텍스트의 역할?
like.get().getMember().getNickname();을 수행한 경우, Member을 조회하는 추가적인 쿼리가 발생했다.Lazy Loading)하면 실제 데이터가 아닌 프록시 객체를 반환한다.post.getMember().getId()와 같이 ID 값만 조회하는 경우에는 추가적인 쿼리를 실행하지 않는다.post.getMember().getNickname() 같은 다른 필드를 조회하면 그제야 SELECT 쿼리가 실행됨 (Lazy Loading 동작).