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 등 추가적인 최적화 기법 활용 가능
'Back-End > JPA' 카테고리의 다른 글
JPA 엔티티 설계 시 VO(Value Object) 패턴 적용하기 (0) | 2025.03.09 |
---|---|
JPA와 NoSQL: MongoDB와 함께 사용 (0) | 2025.03.09 |
JPA 트랜잭션 관리와 @Transactional의 동작 방식 (0) | 2025.03.09 |
JPA랑 Hibernate (0) | 2025.03.09 |
JPA에서 일어나는 영속성 컨텍스트의 동작 원리 (1차 캐시, 변경 감지 등) (0) | 2025.03.09 |