JPA
[JPA 활용] 조회 성능 최적화 - 엔티티 직접 노출🐱
woochii
2023. 11. 4. 15:05
728x90
반응형
지연 로딩과 조회 성능 최적화
- 지연 로딩 때문에 발생하는 성능 문제를 단계적으로 해결하기
- 컬렉션타입을 반환하는 XXXToMany 관계는 다음으로 미루고
XXXToOne (OneToOne, ManyToOne) 관계를 최적화 해보자
V1. 엔티티를 직접 노출
문제점 1
@RestController
@RequiredArgsConstructor
public class OrderSimpleApiController {
private final OrderRepository orderRepository;
@GetMapping("/api/v1/simple-orders")
public List<Order> ordersV1() {
List<Order> all = orderRepository.findAllByString(new OrderSearch());
return all;
}
}
실행한 후 포스트맨으로 가서 Send 버튼을 누르면
에러가 나온다.
StackOverflowError라고 나온다.
원인
- Order(주문) 엔티티는 member를 갖고있다.
- Member(회원) 엔티티는 orders를 갖고있다.
- 서로가 서로를 계속 호출하면서 무한반복에 걸린다.
해결 방법
- 양방향 연관관계에는 이러한 일이 상당히 많이 발생한다.
- 양방향에선 둘 중에 하나는 @JsonIgnore로 끊어줘야 한다.
@Entity
@Getter @Setter
public class Member {
@JsonIgnore
@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<>();
}
문제점 2
다시 포스트맨에서 Send를 눌러보면
다른 서버에러가 뜨는 것을 볼 수 있다.
원인
- 기본적으로 모든 연관관계의 FetchType을 LAZY로 설정해 두었다.
- 그러므로 Order에서 member를 불러올 때 Member 객체가 아닌 프록시 객체를 가져오게 된다.
해결 방법
- 스프링 부트 버전 3.0미만이라면 Hibernate5Module을 3.0이상이면 Hibernate5JakartaModule을 등록하자.
- 프록시가 아닌 진짜 Member 객체를 가져오기 위해 Member의 필요한 속성을 호출한다.
build.gradle
라이브러리 추가 (3.0 이상)
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-hibernate5-jakarta'
JpashopApplication
@Bean
Hibernate5JakartaModule hibernate5JakartaModule() {
Hibernate5JakartaModule hibernate5JakartaModule = new Hibernate5JakartaModule();
// 강제 지연 로딩 설정
// hibernate5JakartaModule.configure(Hibernate5JakartaModule.Feature.FORCE_LAZY_LOADING, true);
return hibernate5JakartaModule;
}
위와 같이 코드를 추가한다.
- 주석을 풀면 강제로 지연 로딩을 설정할 수 있다.
- 그러나 불필요한 속성들까지 호출되기 때문에 쓸 데 없는 쿼리가 많이 나간다. (성능 저하)
- 단, 지연로딩을 피하기 위해 LAZY를 EAGER로 변경하면 절대 안된다.
- 다음과 같은 방법으로 최소한의 성능으로 필요한 속성만 조회하자
@RestController
@RequiredArgsConstructor
public class OrderSimpleApiController {
private final OrderRepository orderRepository;
@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;
}
}
포스트맨에서 실행해보면
주의
- 이러한 방법은 좋지 않은 방법이다. (사용하지 말자)
- 엔티티를 외부로 노출하지 말자.
- 이러한 방법 대신 DTO로 변환해서 반환하자!
출처 : 실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
728x90
반응형