지연로딩, 즉시로딩 (Eager/Lazy Loading)

SangHyun-Park·2022년 2월 16일
0

Spring 을 통해 JPA 를 사용하다보면 연관관계에 있는 Entity 객체 중 하나를 불러올 때 다른 객체 까지도 한꺼번에 불러오는 문제가 발생하는데 그 이유는 JPA가 기본적으로 Entity 를 Fetch 하는 방식이 즉시로딩 이기 때문이다.

즉시로딩

위 Board Entity 를 한번 살펴보자 Member Entity 와 ManyToOne 관계를 가지는 것을 알 수 있고, 아래는 ID가 100 인 Board 를 JPA를 통해서 조회를 했을 때 결과이다

Hibernate 결과를 살펴보면 처음에 의도했던 Board 정보 말고도 연관관계에 있는 Meber 객체까지 join query가 나가는 것을 확인할 수 있다.

Reply 객체는 또 Board 와 연관관계에 있는데 이 Reply 객체를 불러오면 3개의 객체를 조회하는 query 가 한꺼번에 날아가는걸 확인 가능하다. 그만 확인해보자

이렇게 의도치 않은 쿼리가 나가는 이유는 N+1 문제라고도 하는데, 객체(JPA) 입장에서는 두 Entity가 초기화되는 것이 데이터베이스 입장에서는 두 개의 테이블이 생성되는 것과 같기 때문에 join 이 필요하다.

지연로딩

반대로 LAZY Fetch 전략은 어떨까

위와 같이 Member 에 대한 query 는 더이상 나가지 않는다는 것을 알 수 있다.

그렇다면 Board Entity 에서 Member Entity field 는 어떻게 처리했을까? 아무 객체도 생성되지 않은채 비어있는걸까? 그건 말이 안된다. 비어있다면 Lazy 전략을 사용할 때 Board Table 에 존재하는 Member 를 조회하지 못한다는 것을 의미하고 그렇다면 Lazy Loading 을 할 의미가 없다는 것 이다.

실제로 Lazy Loading 후 find 메소드를 사용해 Member 객체를 조회해보면 이번에는 join 문이 포함되어 잘 나간다는 것을 알 수 있다.

단 Testing 환경에서 JpaRepository 사용 시 데이터베이스와의 영속성이 끊긴상태로 Entity 를 불러오므로 꼭 메소드에 @Transactional 을 추가해주어야한다.

이렇게 지연로딩이 동작할 수 있는 이유는 지연로딩이 내부적으로 Member 객체에 대해 Proxy 객체를 주입해주기 때문이다.

Proxy 객체란 껍데기 객체로 생각하면 편한데, 우선적으로 Member 객체가 요구하는 field 를 모두 포함하는 객체이고 실제 Member Entity 대신 주입되어있다가 Member 객체에 대한 직접적인 조회 method 가 호출되면 그제서야 데이터베이스에 join 문을 포함한 query 가 나가게 된다.

단, Lazy Loading 시에도 결국 하위 Entity 를 조회할 때 또 다시 N+1 문제가 발생하는데, 여기서 알 수 있는 것은 N+1 문제라는 것이 fetch type 때문에 발생하는 것이 아니라는 것이다.

사실 N+1 문제는 JPQL 의 한계에서 오는 문제이다. 기본적으로 JPQL 은 repositoy 의 메소드를 fetch type 을 고려하지않고 일관적으로 query 를 생성하는데, 가령 findAll() -> select * from table 의 방식으로 데이터베이스와 바인딩을 수행한다. 그 다음에 JPA가 fetch 전략에 따라 추가적인 query를 날릴지 결정하므로 근본적인 N+1 대처 전략이 아니라는 것이다.

이 문제는 Batch size 를 조절하거나 직접 JPQL로 fetch join 문을 작성하는 방식으로 어느정도(여전히 단점은 존재하기 때문에 작업 상황에 맞게 채택하여 사용해야한다) 해결할 수 있다.

정리

fetch.Eager 은 미리 연관관계 바인딩을 수행하겠다는 것이고
fetch.Lazy 는 단지 바인딩을 하지 않고 가짜 객체(proxy) 를 대신 넣겠다는 의미이다.

그렇기 때문에 바인딩(join)이 필요없는 경우에는 Eager 이 문제가 될거고
그렇다고 Lazy 전략을 써도 개발환경에서 결국 proxy 를 통해 연관관계에 있는 객체를 조회하려면 바인딩이 안되어있어 발생하는 N+1 문제를 만날 수 밖에 없다. 따라서 근본적인 해결을 위해서는 상황에 맞는 대처법(join/fetch join/batch size 등) 을 채택해야한다

profile
https://ppaksang.tistory.com/ 옮겼습니다 !!

0개의 댓글