엔티티를 API 컨트롤러에 직접 노출하면 안된다. 직접 노출할 경우 다음과 같은 문제가 발생한다.
// APi Controller
private final OrderRepository orderRepository;
@GetMapping("/api/v1/simple-orders")
public List<Order> ordersV1(){
List<Order> all = orderRepository.findAllByString(new OrderSearch());
return all;
}
Api 컨트롤러 클래스에서 Order 엔티티 클래스로 데이터를 받은 후 직접 노출한 코드
이대로 실행할 경우 첫번째 문제로 무한루프가 발생한다.
Order 엔티티 클래스에 Member
와 Member 클래스 엔티티에 List<Order>
가 서로 호출하고 있기 때문에 무한루프가 발생한다.
@JsonIgnore 어노테이션을 사용하려는 엔티티 클래스 반대편 엔티티 클래스에 지정해주면 된다.
@JsonIgnore
어노테이션으로 무한루프 문제를 해결해도 아래와 같은 문제가 발생한다.
이 문제는 Order 엔티티 클래스에 Member가 지연로딩으로 되어 있기 때문에 new Member()
로 객체가 초기화 되는게 아니라 new ProxMember()
객체로 초기화 된다. 정확히는 new ByteBuddyInterceptor()
객체로 초기화 된다. 그렇기 때문에 오류가 발생한다. 이 문제를 해결하기 위해서는 Hibernate5Module 객체를 빈에 등록해줘야 한다.
build.gradle
에 다음 라이브러리를 추가
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-hibernate5'
SpringBootApplication
에 Hibernate5Module를 빈으로 등록
@SpringBootApplication
public class JpashopApplication {
public static void main(String[] args) {
SpringApplication.run(JpashopApplication.class, args);
}
@Bean
Hibernate5Module hibernate5Module(){
Hibernate5Module hibernate5Module = new Hibernate5Module();
return hibernate5Module;
}
}
기본적으로 초기화된 프록시 객체만 노출하고 초기화 되지 않은 프록시 객체는 노출하지 않는다.
실행은 되지만 프록시 객체는 null 값으로 초기화 된다. (지연 로딩이 일어나지 않는다.)
지연 로딩을 실행하기 위한 방법으로 Hibernate5Module 빈 설정에 위와 같은 설정을 해주면 된다. 하지만 이러한 방법은 모든 지연 로딩으로 설정된 엔티티를 실행 하기 때문에 필요하지 않는 엔티티도 실행되는 문제가 발생한다 그렇기 때문에 Hibernate5Module에서 설정하는 것 보다 아래와 같은 방법으로 필요한 엔티티만 지연로딩 할 수 있게 해주는게 바람직하다.
주위: 지연 로딩(LAZY)을 피하기 위해 즉시 로딩(EARGR)으로 설정하면 안된다. 즉시 로딩 때문에 연관관계가 필요 없는 경우에도 데이터를 항상 조회해서 성능 문제가 발생할 수 있다. (N + 1 문제 등) 즉시 로딩으로 설정하면 성능 튜닝이 매우 어려워진다.
항상 지연로딩을 기본으로 하고, 성능 최적화가 필요한 경우 패치조인(fetch join)을 사용하자