
Lazy Loading은 실제로 필요한 시점까지 데이터를 "지연"하여 조회하는 방식으로, 데이터베이스와 연결된 Entity 객체들을 필요할 때만 가져와 성능을 최적화하려는 기법입니다.
예를 들어, Comment가 Todo와 관계가 있는 상황에서 FetchType.LAZY를 설정하면 Comment 객체를 조회할 때 Todo는 즉시 불러오지 않습니다. Todo가 실제로 필요해지는 시점에 비로소 데이터베이스에서 쿼리를 실행하게 됩니다.
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "todo_id")
private Todo todo;
이 설정을 통해 Comment를 조회하더라도 Todo는 필요해질 때까지 조회하지 않게 되므로, 불필요한 데이터베이스 호출을 줄일 수 있습니다.
Proxy 객체가 Lazy Loading의 핵심입니다. Comment를 조회할 때 Todo는 즉시 사용되지 않기 때문에, Todo 자리에는 대신 Proxy 객체가 들어갑니다.
Proxy 객체는 진짜 데이터가 필요해지는 순간 데이터베이스에서 데이터를 조회하여 가져옵니다.
예를 들어, Comment를 조회한 후 Todo의 title을 참조하게 되면 그때서야 Todo의 데이터가 데이터베이스에서 조회됩니다.
LazyInitializationException은 Lazy Loading을 사용할 때 세션(Session)이 없을 때 발생하는 오류입니다.
public CommentResponse retrieveCommentById(Long commentId) {
Comment comment = commentService.retrieve(commentId);
return CommentResponse.from(comment); // 여기서 Lazy Loading 발생!
}
위 코드에서 commentService.retrieve()가 트랜잭션 범위를 벗어나면, 영속성 컨텍스트도 사라져서 Comment와 연결된 Todo의 데이터에 접근할 때 LazyInitializationException이 발생할 수 있습니다.
Lazy Loading이 필요한 시점이 트랜잭션 내부로 유지되도록 코드를 수정하는 것입니다.CommentService에서 필요한 데이터를 미리 조회하여 반환하도록 리팩토링하는 방식입니다.LazyInitializationException이 발생하지 않습니다.spring:
jpa:
open-in-view: false # OSIV 비활성화 설정
결론: Lazy Loading, LazyInitializationException, OSIV까지 살펴본 결과, 무작정 옵션을 켜두기보다는 필요한 시점에 트랜잭션 내에서 적절하게 데이터를 조회하도록 하는 것이 중요하다는 점을 확인할 수 있었습니다. 📝