1. 지연 로딩(Lazy Loading)과 즉시 로딩(Eager Loading)이란?

JPA에서는 엔티티를 조회할 때 연관된 엔티티를 언제 로딩할지를 결정하는 방식이 두 가지가 있습니다.

  • 지연 로딩(Lazy Loading): 연관된 엔티티를 실제로 사용할 때 쿼리를 실행하여 가져오는 방식
  • 즉시 로딩(Eager Loading): 엔티티를 조회할 때 연관된 엔티티를 한 번에 함께 가져오는 방식

JPA에서는 @OneToMany, @ManyToOne, @OneToOne, @ManyToMany 등의 관계에서 fetch 속성을 사용하여 로딩 전략을 설정할 수 있습니다.

@ManyToOne(fetch = FetchType.LAZY)
private Team team; // 지연 로딩

@OneToMany(mappedBy = "team", fetch = FetchType.EAGER)
private List<Member> members; // 즉시 로딩

2. 지연 로딩(Lazy Loading)

지연 로딩 동작 방식

  • 엔티티를 조회할 때 연관된 엔티티를 프록시 객체로 설정
  • 실제로 해당 객체를 사용할 때 쿼리를 실행하여 데이터 조회
  • 불필요한 데이터 로딩을 방지하여 성능 최적화 가능

지연 로딩 예제

@Entity
public class Member {
    @Id @GeneratedValue
    private Long id;
    private String name;
    
    @ManyToOne(fetch = FetchType.LAZY)
    private Team team; // 지연 로딩 적용
}
Member member = entityManager.find(Member.class, 1L); // SELECT * FROM member
Team team = member.getTeam(); // team을 사용할 때 SELECT * FROM team 실행

장점

✔ 필요할 때만 데이터를 불러오기 때문에 불필요한 데이터 로딩을 방지할 수 있음

✔ 성능 최적화 가능 (특히, 불필요한 조인 방지)

단점

⚠ 연관된 데이터를 여러 번 조회할 경우 N+1 문제 발생 가능

team.getName()을 호출할 때 쿼리가 실행되므로, 즉시 로딩보다 지연되는 경우 발생


3. 즉시 로딩(Eager Loading)

즉시 로딩 동작 방식

  • 엔티티를 조회할 때 연관된 엔티티까지 한 번에 가져옴
  • JOIN을 사용하여 한 번의 쿼리로 데이터를 조회

즉시 로딩 예제

@Entity
public class Member {
    @Id @GeneratedValue
    private Long id;
    private String name;
    
    @ManyToOne(fetch = FetchType.EAGER)
    private Team team; // 즉시 로딩 적용
}
Member member = entityManager.find(Member.class, 1L); // SELECT m.*, t.* FROM member m JOIN team t
Team team = member.getTeam(); // 추가 쿼리 실행 없이 사용 가능

장점

✔ 연관된 엔티티를 한 번에 로딩하므로 즉시 사용할 때 추가 쿼리가 실행되지 않음

JOIN FETCH를 사용할 경우 N+1 문제를 줄일 수 있음

단점

⚠ 항상 연관된 엔티티를 함께 로딩하기 때문에 불필요한 데이터까지 불러와서 성능 저하 발생 가능

⚠ 연관된 데이터가 많아지면 JOIN 쿼리의 성능 부담 증가


4. 지연 로딩과 즉시 로딩의 활용법

(1) 기본적으로는 지연 로딩(Lazy Loading) 사용

  • 즉시 로딩은 불필요한 데이터를 가져올 가능성이 높아 성능 저하를 일으킬 수 있음
  • 대부분의 경우 FetchType.LAZY를 사용하고, 필요할 때만 조인(Fetch Join) 적용

(2) 즉시 로딩이 유용한 경우

  • 항상 연관된 데이터를 함께 조회해야 할 경우
  • 데이터의 크기가 작고, 조인을 통해 얻는 성능 이점이 있는 경우

(3) Fetch Join을 활용한 성능 최적화

즉시 로딩을 무조건 설정하는 것보다, 필요한 경우 JPQL에서 JOIN FETCH를 사용하여 최적화 가능

List<Member> members = entityManager.createQuery(
    "SELECT m FROM Member m JOIN FETCH m.team", Member.class)
    .getResultList();

이렇게 하면 FetchType.LAZY를 유지하면서도 N+1 문제를 해결할 수 있음.


5. 결론

✔  기본적으로 지연 로딩을 사용하여 불필요한 데이터 조회를 방지해야 함

✔  즉시 로딩은 항상 연관된 데이터를 함께 조회해야 할 경우에만 사용

✔  N+1 문제 해결을 위해 Fetch Join을 적극 활용

✔  필요에 따라 EntityGraph, Batch Size 등 추가적인 최적화 기법 활용 가능

+ Recent posts