본문 바로가기
정리 { Java 백엔드 }/(인강) JPA 프로그래밍 +스프링부트

(JPA) <6> [조회 성능 최적화] _ V1. "엔티티직접노출"의 경우

by 해피퀄리아 2023. 4. 10.

❰❰ API 개발 고급 - 지연 로딩과 조회 성능 최적화 ❱❱

도입

간단한 주문조회의 뜻

주문에 대해서

“주문+주문자+배송정보” 를 조회하자.

최적화 목표

  • 지연로딩 기능으로 인한 문제를 해결해보자.
  • 여러가지 방법들을 단계적으로 검토하여 장단점을 알아보자.

 

< 간단한 주문조회 >

[ (class) OrderSimpleApiController 만들기 ]

/* 클래스 */
@RestController
@RequiredArgsConstructor
public class OrderSimpleApiController {
    private final OrderRepository orderRepository;

}

 

< 간단한 주문조회V1: 엔티티를 직접 노출 >

[ 살펴볼 점 ]

  • 문제는 무엇인가?
  • 해결책은 무엇인가?
  • 해결책의 한계는 무엇인가?

 

[ V1: 엔티티를 직접 노출하는 메서드 ]

/* 엔티티 그대로 반환 */
//Lazy강제 초기화도 없이.
@GetMapping("/api/v1/simple-orders")
public List<Order> ordersV1() {
    List<Order> all = orderRepository.findAllByString(new OrderSearch());
    return all;

 

[ 0 ] 엔티티를 그대로 노출하는 경우, 문제점 2가지

  • 1) 순환참조 문제
  • 2) 지연로딩으로 인한 프록시엔티티 조회문제

 

[ 1 ] 순환참조 문제

 

문제점 생기는 이유

(Jackson라이브러리가 엔티티→ Json형태로 만들게 된다.)

그래서 엔티티를 조회하는데,,,

엔티티 내부에 엔티티도 조회하게 된다.

그런데, Order클래스에 Member 엔티티, Delivery 엔티티가 있다.

또한, 내부 엔티티가 원래의 엔티티를 참조하고 있다. (Order → Member → Order →

순환

)

→ 즉 순환구조이다.

따라서 계속 순환하면서, Json을 만들려고 하기에, 무한순환한다.

 

해결책과 한계점

  • @JsonIgnore 어노테이션 걸어주기즉 원래 Order클래스로 돌아가는 참조에다가 JsonIgnore 걸어주기
  • → 순환안되게, 되돌아가는데 걸어주기
  • 한계점
    • API스펙 변경 어려운 점들. 다양한 API스펙을 만들 수 없는 것들

 

[ 2 ] 지연로딩에서, 빈 깡통 프록시엔티티 조회

문제점

지연로딩인 경우, DB직접조회하지 않고(즉 지연하고)

하이버네이트에서 프록시엔티티를 생성해서 넣어준다.

(byteBuddy 라이브러리가 프록시엔티티를 만들어준다. 프록시엔티티 : ByteBuddyInterceptor 클래스)

→ 즉 초기화되지 않은 빈 깡통인 프록시객체를 조회하면 에러가 난다.

 

해결책과 한계점

4가지

  • 해결책(1) 특정 모듈 사용하여 → null로 조회하기
    • → 초기화되지 않은 프록시객체를 제외함. 초기화된 것만 노출함.
    • 특정 모듈을 스프링 빈으로 등록하면 해결
    • 모듈 등록 코드
```java
/* build.gradle 에*/
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-hibernate5-jakarta'

///////////////////////////////////////////
/* java */
@Bean
Hibernate5JakartaModule hibernate5Module() {
	return new Hibernate5JakartaModule();
}
```

 

  • 해결책(2) 특정 모듈로 → 강제 로딩하여 DB조회하기
    • 한계점 : 성능낭비가 생긴다. 쓸모없이 모두 다 DB조회하니까.
  • 해결책(3) : 직접 강제초기화 명령코드 날리기
    • 한계점 : 엔티티 직접노출의 전형적 한계
      • 쓸데없이 성능낭비 및 복잡한 API Json출력
      • API스펙 변경 문제
    • 강제초기화 명령코드
    •  
    • /* 직접 강제초기화 명령코드 날리기 */ @GetMapping("/api/v1/simple-orders") public List<Order> ordersV1() { List<Order> all = orderRepository.findAllByString(new OrderSearch()); for (Order order : all) { order.getMember().getName(); //Lazy 강제 초기화 order.getDelivery().getAddress(); //Lazy 강제 초기환 } return all; }
  • 해결책(4) : EAGER조건 하기
    • 한계점 : 성능낭비하고, 성능튜닝이 어렵다.

댓글