오랜만에 Spring JPA 공부를 해보자 ㅎㅎ
지난 내용은 여기 참고
이번 시간에는 주문 + 배송정보 + 회원을 조회하는 API
만들어볼 것이다.
지연 로딩 때문에 발생하는 성능 문제를 단계적으로 해결해보자.
실무에서 JPA를 사용하려면 꼭 필요한 내용이므로 집중 ❗❗
OrderSimpleApiController
에 코드 추가@RestController
@RequiredArgsConstructor
public class OrderSimpleApiController {
private final OrderRepository orderRepository;
@GetMapping("/api/v1/simple-orders")
public List<Order> orderV1() {
List<Order> all = orderRepository.findAllByString(new OrderSearch());
return all;
}
}
위와 같이 코드를 작성한 뒤, http://localhost:8080/api/v1/simple-orders
로 접속하면
다음과 같이 엔티티가 무한으로 호출되는 결과를 확인할 수 있다.
이는 order
와 member
가 서로를 참조하기 때문에 무한순회를 하기 때문이다.
위와 같이 무한순회를 방지하기 위해서는 @JsonIgnore
를 추가해주어야 한다.
Member.java
@JsonIgnore
@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<>();
OrderItem.java
@JsonIgnore
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_id")
private Order order; //주문
Delivery.java
@JsonIgnore
@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<>();
위와 같이 @JsonIgnore
를 추가해주자!
다시 http://localhost:8080/api/v1/simple-orders
로 접속하면 다음과 같이 출력되는 다른 문제가 발생한다!
ByteBuddyInterceptor
에서 Type definition error
가 발생하였다.
order
→ member
, address
는 지연 로딩이므로 실제 엔티티가 아닌 프록시 객체가 존재한다. jackson
라이브러리는 기본적으로 이 프록시 객체를 json으로 생성하는 방법을 모른다 🤷🏻♀️ 그래서 이와 같은 예외가 발생하는 것이다.
Hibernate5Module
등록위와 같은 문제를 해결하기 위해 Hibernate5Module
을 스프링 Bean으로 등록해주면 된다!
먼저 build.gradle
에 다음과 같은 라이브러리를 추가해준 뒤,
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-hibernate5'
JpashopApplication
에 다음과 같은 코드를 추가해주자.
@Bean
Hibernate5Module hibernate5Module() {
return new Hibernate5Module();
}
그러면 이제 다음과 같이 잘 출력된다.
@JsonIgnore
처리해주기@JsonIgnore
처리해야 한다!Hibernate5Module
를 사용하기 보다는 DTO로 변환해서 반환하는 것이 좋은 방법이다!fetch join
)을 사용해라! → V3OrderSimpleApiController
- V2 추가@GetMapping("/api/v2/simple-orders")
public List<SimpleOrderDto> orderV2() {
return orderRepository.findAllByString(new OrderSearch()).stream()
.map(SimpleOrderDto::new)
.collect(toList());
}
@Data
static class SimpleOrderDto {
private Long orderId;
private String name;
private LocalDateTime orderDate;
private OrderStatus orderStatus;
private Address address;
public SimpleOrderDto(Order order) {
orderId = order.getId();
name = order.getMember().getName();
orderDate = order.getOrderDate();
orderStatus = order.getStatus();
address = order.getDelivery().getAddress();
}
}
1 + N + N
번 호출된다.order
조회 1번order → member
지연 로딩 조회 N번order → delivery
지연 로딩 조회 N번
Order
조회가 많아질수록 쿼리 수행이 많아지고 성능 저하가 일어날 수 있다!