1 분 소요

지연 로딩과 조회 성능 최적화

주문 + 배송정보 + 회원을 조회하는 API 를 만들자. 지연 로딩 때문에 발생하는 성능 문제를 단계적으로 해결해 보자

간단한 주문 조회 v1 : 엔티티를 직접 노출

1
2
3
4
5
6
7
8
9
10
/*  
* 엔티티를 직접 반환해 보자 (하면 안되!!)  
*  
* */  
@GetMapping("/api/v1/simple-orders")  
public List<Order> ordersV1() {  
    List<Order> all = orderRepository.findAllByString(new OrderSearch());  
    return all;  
}

Order 가면 Member 조회 > Member 가면 Order 조회 > 이걸 무한 루프 ;;

해결 방법

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Member.java

@JsonIgnore  
@OneToMany(mappedBy = "member")  
private List<Order> orders = new ArrayList<>();

// OrderItem.java
@ManyToOne(fetch = FetchType.LAZY)  
@JoinColumn(name = "order_id")  
@JsonIgnore  
private Order order;

// Delivery.java
@OneToOne(mappedBy = "delivery", fetch = FetchType.LAZY)  
@JsonIgnore  
private Order order;

이렇게 @JsonIgnore 로 양방향 연관관계 를 끊어줘야한다.

그래도 다음과 같이 오류 발생

Order.java 에 Member는 지금 Lazy 로 되있음. → proxy 객체임.

Hibernate5Module 이 필요

1
2
3
4
5
6
7
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-hibernate5'


@Bean  
Hibernate5Module hibernate5Module() {  
    return new Hibernate5Module();  
}

근데 이렇게 해봤자 엔티티를 직접 클라이언트한테 노출하는 건 올바른 행위가 아니다. DTO 설계해서 감싸서 리턴 하는 것이 당연한 행위다. 이건 알고만 있자.

참고 : 앞에서 계속 강조 했듯이 정말 간단한 애플리케이션이 아니면 엔티티를 API 응답으로 외부로 노출하는 것은 좋지 않다. 따라서 Hibernate5Module을 사용하기 보다는 DTO 로 변환하여 반환하는 것이 더 좋은 방법이다.

주의 : 지연 로딩(Lazy) 를 피하기 위해서 즉시 로딩 (EAGER)로 설정하면 안된다! 즉시 로딩 때문에 연관관계가 필요 없는 경우에도 데이터를 항상 조회해서 성능 문제가 발생할 수 있다. 즉시 로딩으로 설정하면 성능 튜닝이 매우 어려워진다. 항상 지연 로딩을 기본으로 하고, 성능 최적화가 필요한 경우에는 페치 조인(fetch join)을 사용해라(V3 에서 설명)

댓글남기기