본문 바로가기
개념정복💫/스프링 Spring 정복

즉시 로딩(EAGER)과 지연 로딩(LAZY), 언제 어떤 전략을 써야 할까?

by 옹쑥이 2025. 2. 23.

JPA에서는 @ManyToOne, @OneToMany, @OneToOne, @ManyToMany 같은 연관 관계를 맺을 때,
데이터를 언제 로딩할지 결정하는 FetchType 옵션이 있습니다.

  • 즉시 로딩(FetchType.EAGER): 연관된 엔티티를 즉시 함께 조회
  • 지연 로딩(FetchType.LAZY): 연관된 엔티티를 실제 사용할 때 조회

1️⃣ 왜 기본적으로 LAZY가 권장될까?

즉시 로딩(FetchType.EAGER)의 문제점

즉시 로딩은 편리하지만 항상 모든 연관된 엔티티를 함께 불러온다는 점이 문제가 됩니다.
즉, 필요 없는 데이터를 가져오거나, 쿼리가 복잡해지면서 성능 저하가 발생할 수 있어요.


EAGER 사용 시 발생할 문제들

1️⃣ 불필요한 데이터까지 한 번에 조회됨

  • 예를 들어, User 엔티티가 List<Order>를 즉시 로딩(EAGER)로 설정했다면,
    사용자가 로그인할 때마다 모든 주문 내역까지 함께 불러오는 불상사가 생길 수 있어요.

2️⃣ JPQL 사용 시 예기치 않은 추가 JOIN 발생

  • SELECT u FROM User u WHERE u.name = 'Kim' 같은 간단한 JPQL을 실행할 때,
    연관된 엔티티를 EAGER로 설정했다면, 자동으로 JOIN이 추가되어 성능이 급격히 떨어질 수도 있습니다.

3️⃣ N+1 문제 발생 가능성

  • 즉시 로딩이 설정된 연관 엔티티가 또 다른 연관 관계를 가지고 있다면,
    select * from order where user_id = ? 같은 쿼리가 반복적으로 실행될 수 있어요.
    (N+1 문제라고 불리는 JPA의 대표적인 성능 이슈입니다.)

📌 💡 그래서 기본적으로 LAZY가 권장됨

  • LAZY를 사용하면 연관된 데이터를 진짜 필요한 순간에만 가져올 수 있기 때문입니다.
  • 즉, 최소한의 데이터만 불러오는 것이 기본 원칙이고,
    연관된 엔티티가 꼭 필요할 때만 추가적인 쿼리를 실행하는 방식이 더 효율적이에요.

2️⃣ 그럼 언제 EAGER를 써야 할까?

즉시 로딩(EAGER)이 적절한 경우

1️⃣ 자주 사용하는 데이터이고, 항상 함께 조회하는 경우

  • 예를 들어, User와 UserProfile처럼 거의 같이 쓰이는 데이터라면
    지연 로딩(LAZY)보다는 즉시 로딩(EAGER)이 더 적절할 수 있어요.

2️⃣ 연관된 데이터 개수가 많지 않은 경우

  • OneToOne 관계에서는 즉시 로딩을 써도 큰 문제가 되지 않는 경우가 많습니다.
    (Order와 Payment 같은 관계에서는 EAGER를 사용해도 무방)

3️⃣ 항상 특정 데이터와 함께 사용해야 하는 경우

  • 예를 들어, Board와 BoardCategory처럼 특정 데이터는
    무조건 함께 사용해야 한다면 즉시 로딩이 편리할 수도 있어요.

3️⃣ 성능 최적화를 위해 추가로 고려할 것들

페치 조인(Fetch Join) 활용하기

  • 즉시 로딩(EAGER)을 사용하지 않고도 연관된 엔티티를 함께 조회하는 방법이 있습니다.
  • JPQL에서 fetch join을 활용하면, 필요한 경우에만 연관 데이터를 한 번에 가져올 수 있어요.

예시: EAGER 대신 fetch join으로 최적화

@Query("SELECT u FROM User u JOIN FETCH u.orders WHERE u.id = :userId")
User findUserWithOrders(@Param("userId") Long userId);

📌 JOIN FETCH를 사용하면 불필요한 추가 쿼리 없이 한 번의 조회로 데이터를 가져올 수 있음!
💡 이렇게 하면 EAGER를 사용하지 않고도 즉시 로딩 효과를 낼 수 있습니다.


배치 크기 조정(@BatchSize)

  • 만약 List<Order>처럼 컬렉션 데이터를 지연 로딩(LAZY)로 가져와야 한다면,
    @BatchSize를 이용해 쿼리 개수를 줄일 수 있습니다.

컬렉션 데이터 최적화 (N+1 문제 방지)

@Entity
public class User {
    @OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
    @BatchSize(size = 10) // 💡 한 번에 10개씩 가져오기
    private List<Order> orders;
}

📌 이렇게 하면 지연 로딩 시 10개씩 묶어서 한 번에 조회하여 성능을 개선할 수 있어요.


4️⃣ 결론: LAZY를 기본으로, 상황에 따라 EAGER 또는 최적화 적용!

연관 관계 기본 설정 추천 방식
@ManyToOne LAZY (권장) fetch join 활용
@OneToMany LAZY (권장) @BatchSize로 최적화
@OneToOne EAGER 또는 LAZY 사용 패턴에 따라 선택
@ManyToMany LAZY (권장) @JoinTable 활용

💡 무조건 LAZY만 쓰는 것도 답이 아니고,
💡 무조건 EAGER를 쓰는 것도 답이 아닙니다.

 

🔹 어떤 데이터가 자주 함께 사용되는지
🔹 쿼리 성능을 어떻게 최적화할 수 있는지
🔹 N+1 문제를 피할 방법이 있는지

 

이런 요소들을 실제 데이터 사용 패턴에 맞춰 선택하는 것이 가장 중요합니다! 🚀