2025년 04월 29일
Spring Data JPA를 쓰다 보면 반드시 부딪히는 주제 중 하나가 바로 Lazy Loading
과 Eager Loading
입니다.
처음에는 그냥 잘 돌아가니까 별 생각 없이 지나치지만, 어느 순간 "왜 이렇게 쿼리가 많이 나가지?" 싶은 순간이 찾아옵니다.
데이터가 100개 200개 정도라면 무시해도 괜찮겠지만, 전체 유저를 조회하는 등 몇십만, 몇백만의 데이터를 조회한다면 이 쿼리는 기하급수적으로 늘어나므로 결코 무시할 수 없죠.
실제 객체가 필요한 시점까지 쿼리를 미룬다.
ToMany
관계의 Default 로딩 전략엔티티를 조회할 때 연관된 객체까지 함께 가져온다.
ToOne
관계의 Default 로딩 전략연관 객체가 하나인 ToOne
관계에서 연관 객체에 접근할 때는 Lazy
로딩을 사용하되, Fetch Join
으로 미리 조회 하는 것이 성능상 유리하다.
@Query("SELECT post FROM Post post JOIN FETCH post.user WHERE post.id = :postId")
Optional<Post> findPostWithAuthor(@Param("postId") Long postId);
ToMany 관계에서 Lazy Loading을 사용할 떄는 N+1을 주의해야 한다.
ex) 특정 유저가 작성한 게시글에 좋아요를 누른 유저 전부를 조회하려는 상황
즉, 제대로 최적화하지 않으면 몇 만, 몇 십만 개의 쿼리가 한 번에 터질 수 있습니다.
컬렉션 관계에서 Fetch Join
으로 한번에 조회한다면, 많은 양의 데이터를 한번에 조회하기 때문에 데이터베이스에 가는 부하를 예상할 수 없어 위험하다.
또한 Fetch Join
은 조인 결과 row 수가 늘어나기 때문에, LIMIT이나
OFFSET을
걸면 “원래 가져오려던 기준”과 다르게 페이징이 깨져 페이징 처리가 불가능하다.
그래서 컬렉션 ToMany 관계에서는 BatchSize
를 이용해서 데이터 조회를 최적화 할 수 있다.
@BatchSize(size = 100)
@OneToMany(mappedBy = "user")
private List<Post> posts;
이렇게 BatchSize
를 100으로 설정한다면 유저의 Lazy Loading
을 통해 post를 조화 할 때 하나씩 조회하는 것이 아닌, 100개씩
조회 한다는 말이다.
기존에는 post에 접근 할 때마다 하나씩 조회를 했다면 이번에는 100개를 미리 조회함으로써 한번의 쿼리로 충분해진 것이다. 만약 post가 200개라면? 100번째 post까지는 추가 로딩 X, 101번째 post 조회 시 100개 추가 조회
ToOne 관계 -> Lazy Loading + JPQL Fetch Join
ToMany 관계 -> Lazy Loading + Batch Size
© 2024-2025.
Heeyeon Lee
all rights reserved.