Spring

[JPA] 즉시 로딩(FetchType.EAGER)과 지연 로딩(FetchType.LAZY)

728x90

Spring Data JPA (이후 줄여서 JPA)를 사용할때 우리는 엔티티를 영속성 컨테스트에 저장하고 flush 해주는 방식으로 DB에 저장했습니다. 엔티티가 RDB의 테이블과 맵핑된 경우, 모든 테이블이 독립적이지 않아서 필요에 따라 테이블을 join 해서 사용하는 경우가 자주 발생합니다.

 

이럴 경우 우리는 엔티티에 @OneToOne, @ManyToOne, @OneToMany, @ManyToOne 어노테이션을 이용해서 알려줍니다. 예를 들어 아래 코드를 한번 볼까요?

@Getter
@Entity
public class Classroom {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  
  private String classroomName;
}
@Getter
@Entity
public class Student {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  
  private String studentName;
  
  @ManyToOne
  @JoinColumn(name = "classroom_id")
  private Classroom classroom;
}

Student와 Classroom 사이가 N:1 즉 @ManyToOne 관계로 맵핑이 되어있는 것을 볼 수 있습니다.

 

이 경우 만약 JPA repository를 이용해서 DB에서 Student 정보를 불러올때마다 맵핑된 Classroom 객체도 같이 가져올까요? 나는 학생 이름만 알고싶은데 계속 Classroom 객체를 같이 불러와야할까요?

 

이처럼 상황에 따라서 Classroom 객체가 필요한 경우가 있고 Student 객체만 필요한 경우가 있겠죠~! 이를 위해 지연로딩과 즉시로딩을 알아봅시다


지연 로딩 (FetchType.LAZY)

@ManyToOne 어노테이션에는 fetch 라는 옵션을 설정할 수 있습니다. 따라서 지연 로딩을 설정하려면 아래 코드처럼 해주면 됩니다.

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "classroom_id")
private Classroom classroom;

 

지연이라는 뜻에서 알 수 있듯 지연로딩 설정을 해주면 맵핑된 객체를 한박자 늦게 가져옵니다. Student 엔티티를 불러온 순간 같이 불러오는게 아니라, 불러온 Student 객체에서, getClassroom() 같은 getter 메소드로 직접 Classroom 엔티티를 호출할때 그때 가져오게됩니다. 따라서 SQL 쿼리가 두번 나뉘어서 이루어집니다.

  • Student 객체를 불러올때
  • Student 객체 안에 있는 Classroom 객체를 불러올 때

이렇게 하면 아까 위에서 고민했던 것 처럼, Student 엔티티에서 학생의 이름만 쓰고싶을때 굳이 필요없는 Classroom 엔티티를 다른 테이블과 join 해서 동시에 가져오지 않아도 되는것입니다!

 

어떻게 이게 가능할까요? 바로 Proxy 객체를 이용하기 때문입니다. 여기서 프록시 패턴에 대해 한번 다뤘었는데요. 결국 Student 객체를 DB 조회하면, Lazy 로딩이 설정돼있는 Classroom 엔티티는 프록시 객체가 대체합니다. 그리고 해당 프록시객체를 호출하면 실제 Classroom을 가져오기 위해 다시한번 SQL 쿼리를 날리게 되는거죠. 


즉시 로딩 (FetchType.EAGER)

즉시 로딩은 바로 아시겠죠? 엔티티를 불러올때 맵핑된 엔티티를 바로 불러옵니다.

@ManyToOne(fetch = FetchType.EAGER) // @ManyToOne에서 default가 EAGER여서 안써줘도된다 
@JoinColumn(name = "classroom_id")
private Classroom classroom;

이렇게 설정하면 학생 엔티티를 불러오는 동시에 Classroom 엔티티를 불러오게 됩니다.


주의할 점

  • 즉시 로딩을 적용하면 예상하지 못한 SQL이 발생할 수 있습니다. 예를 들어 @ManyToOne 맵핑이 5개 있는데 전부 즉시로딩으로 설정되어있다면 항상 테이블 5개를 조인해서 가져오겠죠? 테이블에 따라 엄청난 부하로 DB가 내려갈수도 있습니다.
  • 즉시 로딩은 JPQL에서 N+1 문제를 바로 일으킵니다. (지연 로딩에서도 연관된 엔티티를 조회할 때 발생하긴 합니다). 

 

출처:
- [JPA] 즉시 로딩과 지연 로딩(FetchType.LAZY or EAGER)
728x90