지연 로딩 적용

jihan kong·2023년 2월 16일
0
post-thumbnail

연관된 엔티티를 사용할 때 조회하는 지연 로딩


1. 즉시 로딩의 단점

FetchType.EAGER 로 설정

일대일, 다대일로 매핑할 때 OrderItem Entity 혹은 Order Entity를 조회하려고 한다면 Order Entity의 경우, 자신과 다대일로 매핑된 Member Entity까지 조회하게 된다. 우리 쇼핑몰의 경우, 4개의 테이블을 조인해오지만 실제 비즈니스에서는 매핑되는 Entity의 개수가 훨씬 더 많을 것이다.


(OrderItem 하나를 조회했는데, 엄청나게 긴 쿼리문들이 나온다.)

그렇게 된다면 개발자는 쿼리가 어떻게 실행될지 예측할 수가 없다. 또한, 사용하지 않는 데이터도 한꺼번에 조회하므로 성능 문제도 있을 수 있다. 따라서 이럴 때는 즉시 로딩이 아닌 지연 로딩을 사용해야 한다.


2. 지연 로딩

OrderItem.java

package com.shop.entity;

import com.shop.constant.OrderStatus;
import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;
import java.time.LocalDateTime;

@Entity
@Getter
@Setter
public class OrderItem {

    @Id
    @GeneratedValue
    @Column(name = "order_item_id")
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "item_id")
    private Item item;              

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "order_id")
    private Order order;
    
    private int orderPrice;

    private int count;

    private LocalDateTime regTime;
    
    private LocalDateTime updateTime;
}
  • FetchType.LAZY 방식으로 지연 로딩 방식을 사용한다.

3. 지연 로딩 방식의 작동 방식


1. 만약, OrderItem을 조회한다고 하면 로딩되는 시점에 Lazy 로딩 설정이 되어있는 Order, Item Entity는 Proxy 객체로 가져오게 됨.
2. 이 후, 실제 객체를 사용하는 시점에 초기화가 됨. (DB에 쿼리가 나감)

즉, 사용되기 전까지는 데이터 로딩을 하지 않고, 실제 사용되는 시점에 조회 쿼리문이 실행된다. 이를 토대로 CartCartItem, Order 도 지연 로딩을 설정해주었다.

4. 배운점

  • 위에서 언급했듯, 실무에서는 가급적 지연 로딩만 사용한다
  • JPA 구현체를 한 번에 가져와서 쓰면 좋지 않을까? 생각을 할 수 있는데
    즉시 로딩을 적용하면 예상하지 못한 SQL이 발생할 수 있다.
  • 즉시 로딩(EAGER) 는 JPQL에서 N+1 문제 를 발생시킨다고 한다.

📌 N + 1 issue ?
1번의 쿼리를 날렸을 때 의도하지 않은 N번의 쿼리가 추가적으로 실행되는 것을 의미한다.

실무에서는 복잡한 쿼리를 사용하기 위해 JPQL을 많이 사용하게 되는데 JPQL에서 만든 SQL을 통해 데이터를 조회할 때, EAGER 전략으로 Fetch하게 되면 해당 데이터의 연관 관계를 가진 하위 엔티티들을 추가적으로 조회하게 되는 것이다.

우리 프로젝트로 예를 들면 OrderItem 을 조회할 때, Order Entity 를 각각 쿼리 날려서 가져오게 되는 것인데 만약 OrderItem 이 수천, 수만 개라면 엄청난 쿼리의 개수가 생성될 것이다.

N + 1 issue 해결 방법

❗ 지연 로딩일 경우 N+1 문제를 해결할 수 있는가?

  • 지연 로딩일 경우, 추가 조회는 하지않지만 하위 엔티티를 가지고 작업하게 되면 추가 조회가 발생하기 때문에 결국엔 N+1 문제가 발생한다.
  • N+1 문제는 FetchJoin, EntityGraph 두 가지 방법으로 해결한다고 한다.
  • FetchJoin : 미리 두 테이블을 JOIN하여 한 번에 모든 데이터를 가져오면 N+1 문제가 발생하지 않는 것에서 착안

<Fetch Join 의 단점>

  • 쿼리 한 번에 모든 데이터를 가져오기 때문에 Paging API(Pageable) 를 사용할 수 없다.
  • 1:N 관계가 두 개 이상이면 Fetchjoin 을 사용할 수 없다.
  • 번거롭게 두 테이블을 JOIN하는 쿼리문을 직접 작성해야 한다.

(출처 : https://dev-coco.tistory.com/165)

profile
학습하며 도전하는 것을 즐기는 개발자

0개의 댓글