[모플 프로젝트] 지연 로딩(Lazy Loading) 문제와 해결 경험 정리

728x90

 

지연 로딩(Lazy Loading) 문제와 해결 경험 정리

문제 상황

댓글 좋아요 토글 기능 `toggleLike` 를 구현하면서 다음과 같은 문제가 발생

 

- 좋아요 토글 이후 `comment.getWriter().getNickname()` 과 같이 지연 로딩된 필드에 접근할 때 `LazyInitializtionException` 발생

- 좋아요 로직 내부에서 `@Modifying(clearAutomatically = true)` 옵션을 사용한 것이 원인

  (좋아요 같은 기능은 동시성 문제 때문에 엔티티 값을 변경해 주는 것이 아니라, 직접 DB 에 update 하였다.)

 

원인 분석 

- `@Modifying(clearAutomatically = true)` 는 DB 수정 쿼리 이후 영속성 컨텍스트를 자동으로 clear 해 준다.

- 이로 인해 기존에 조회했던 `comment` 엔티티는 detached 상태 가 되며, 이후 해당 객체에서 지연 로딩된 필드(Writer) 에 접근할 경우 세션이 닫혀 예외가 발생

 

해결 방법

1. `@Modifying(clearAutomatically = true)` 로 인해 기존 `comment` 는 `detached` 상태가 되므로

2. `commentService` 내부에서 `likeService.toggleLike()` 호출 이후, 다시 commentRepository.findById( ) 로 `comment` 를 재조회

@Transactional
public CommentClientResponse toggleLike(Long userId, Long commentId) {
    PlanComment comment = reader.findComment(commentId);
    reader.findUser(userId);

    boolean likedByMe = likeService.toggleLike(userId, comment);

    // 다시 조회하여 새로운 영속성 컨텍스트에 attach
    PlanComment updatedComment = reader.findComment(commentId);
    List<User> mentionedUsers = mentionService.findMentionedUsers(updatedComment.getId());

    return ofComment(new CommentResponse(updatedComment, mentionedUsers, likedByMe));
}
public PlanComment findComment(Long commentId) {
  return commentRepository.findById(commentId).orElseThrow(
      () -> new ResourceNotFoundException(NOT_FOUND_COMMENT)
  );
}
`reader` 내부에 `findComment` 는 이렇게 정의되어 있다.
 

알게 된 점

- `@Modifying(clearAutomatically = true)` 는 변경 쿼리 후 영속성 컨텍스트를 초기화하여 기존 엔티티들이 detached 상태가 됨
  (이때, 물리 트랜잭션 영속성 컨텍스트가 비워지므로 모든 엔티티 전부 clear)
- 이 상태에서 `Lazy` 로딩 필드에 접근하면 `LazyInitializationException` 예외 발생
- 따라서 같은 물리 트랜잭션 내라면 `@Modifying(clearAutomatically = true)` 를 사용하고 난 뒤, 사용할 엔티티는 다시 조회해서 사용해야 한다
 
- 영속성 컨텍스트는 물리 트랜잭션 단위로 생성되고 공유할 수 있음
 
 
 
 
 
 
 
 
728x90