
JPA에서 실제 데이터가 필요한 시점까지 데이터베이스 조회를 지연하는 기법이다.
엔티티를 처음 조회할 때 연관된 데이터를 즉시 로드하지 않고, 그 연관된 데이터가 실제로 사용될 때 데이터베이스에서 조회하는 방식이다.
-> 불필요한 데이터 조회를 줄여서 성능을 최적하하는데 유리하다.
지연 로딩을 설정하면 연관된 엔티티나 컬렉션은 처음에 프록시 객체로 로드된다.
프록시는 실제 엔티티를 대신하는 객체로, 데이터베이스 조회가 필요할 때 프록시가 실제 데이터를 조회하여 값을 제공한다.
따라서 처음부터 연관된 데이터를 모두 로드하는 것이 아니라 실제 접근 시점에 데이터베이스에서 로드 되도록 지연된다.
@Entity
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "team", fetch = FetchType.LAZY) // 지연 로딩 설정
private List<Member> members = new ArrayList<>();
// getter, setter
}
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "team_id")
private Team team;
// getter, setter
}
Team 엔티티의 members 필드에 fetch = FetchType.LAZY 옵션을 설정했다.
Team 엔티티를 조회할 때 members 리스트는 데이터베이스에서 즉시 로드되지 않으며, members 컬렉션에 실제 접근하는 순간 데이터베이스에서 조회가 이루어진다.
1. 프록시 초기화
2. N + 1
직렬화 문제
→ 지연 로딩된 엔티티를 컨트롤러에서 그대로 반환하면, JSON 직렬화 과정에서 프록시가 초기화되며 예외나 불필요한 쿼리가 발생할 수 있다.
로그·디버깅 문제
→ 엔티티를 로그로 출력하거나 디버깅할 때 toString()이나 getter 호출로 지연 로딩이 실행되어 의도치 않은 DB 조회가 발생할 수 있다.
equals / hashCode 문제
→ equals나 hashCode에서 연관 엔티티에 접근하면 프록시가 초기화되어 성능 저하나 예외가 발생할 수 있으므로 식별자 기준으로만 구현해야 한다.
컬렉션 + 페이징 문제
→ 지연 로딩된 컬렉션을 fetch join과 함께 페이징하면 메모리 페이징이 발생해 심각한 성능 문제가 생길 수 있다.
만능 오해
→ 지연 로딩은 성능 최적화 수단일 뿐 만능이 아니며, 상황에 따라 fetch join이나 DTO 조회와 함께 사용해야 한다.