HEEYEON'S BLOG

💻 개발

#Java
#Spring Boot
#JPA
#Lazy Loading
#Eager Loading

🔥 스프링 JPA, 로딩 방식과 조회 최적화 하기

스프링 JPA의 Lazy 로딩과 Eager 로딩의 차이와, 조회 성능을 최적화해보자

2025년 04월 29일

Spring Data JPA를 쓰다 보면 반드시 부딪히는 주제 중 하나가 바로 Lazy LoadingEager Loading입니다.
처음에는 그냥 잘 돌아가니까 별 생각 없이 지나치지만, 어느 순간 "왜 이렇게 쿼리가 많이 나가지?" 싶은 순간이 찾아옵니다.

데이터가 100개 200개 정도라면 무시해도 괜찮겠지만, 전체 유저를 조회하는 등 몇십만, 몇백만의 데이터를 조회한다면 이 쿼리는 기하급수적으로 늘어나므로 결코 무시할 수 없죠.


Lazy Loading 와 Eager Loading

Lazy Loading

실제 객체가 필요한 시점까지 쿼리를 미룬다.

Eager Loading

엔티티를 조회할 때 연관된 객체까지 함께 가져온다.


Eager Loading 의 단점

그러면 Eager Loading 대신 뭘 써야할까

연관 객체가 하나인 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

ToMany 관계에서 Lazy Loading을 사용할 떄는 N+1을 주의해야 한다.

ex) 특정 유저가 작성한 게시글에 좋아요를 누른 유저 전부를 조회하려는 상황

즉, 제대로 최적화하지 않으면 몇 만, 몇 십만 개의 쿼리가 한 번에 터질 수 있습니다.

그럼 Fetch Join으로 한번에 가져오면 되겠네 !

컬렉션 관계에서 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.