참고 : [실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화] - 김영한
엔티티를 직접 노출하는 방법으로 주문 조회를 구현해보자.
API 핵심 기능
- XToOne (ManyToOne,OneToOne)
- Order 조회
- Order -> Memeber 연관
- Order -> Delivery 연관
~/api/OrderSimpleApiController.java
@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;
}
}
예제의 양방향 연관관계의 경우
Order->Member
Member->Orders
Orders-> Member
의 관계가 성립되기 때문에, Json입장에서는 무한 루프를 돌아 계속해서 객체를 찾게 된다.
Json 직렬화시 양방향 연관관계 해결
양방향이 걸레는데를 모두 둘 중 하나에 @JsonIgnore 로 설정해야한다.
@JsonIgnore 사용: 한 쪽 방향의 필드에 @JsonIgnore을 사용하여 해당 필드가 JSON 직렬화에서 제외되도록 설정
- Member클래스의 orders에 @JsonIgnore을 걸어주자.
- OrderItem클래스의 order에 @JsonIgnore을 걸어주자.
- Delivery클래스의 order에 @JsonIgnore을 걸어주자.

@JsonIgnore을 설정하여 양방향 연관관계를 해결하였지만 위와 같은 오류가 발생한다.
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="member_id")
private Member member; //연관관계 주인
해결 방법
Hibernate5JakartaModule 설정(스프링부트 3.0 이상) : Hibernate 모듈이 자동으로 프록시 객체를 초기화하여 직렬화할 수 있게 해줌.
- build.gradle에 등록
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-hibernate5-jakarta'- JpashopApplication에 빈 등록
@Bean Hibernate5JakartaModule hibernate5Module() { return new Hibernate5JakartaModule(); }
- 기본적으로 초기화 된 프록시 객체만 노출, 초기화 되지 않은 프록시 객체는 노출 안함
앞에서 계속 강조했듯이 정말 간단한 애플리케이션이 아니면 엔티티를 API 응답으로 외부로 노출하는 것은 좋지 않다. 따라서 Hibernate5Module 를 사용하기 보다는 DTO로 변환해서 반환하는 것이 더 좋은 방법이 다.
주의: 지연 로딩(LAZY)을 피하기 위해 즉시 로딩(EARGR)으로 설정하면 안된다! 즉시 로딩 때문에 연관관계가 필요 없는 경우에도 데이터를 항상 조회해서 성능 문제가 발생할 수 있다. 즉시 로딩으로 설정하면 성능 튜닝이 매우 어려워 진다.
항상 지연 로딩을 기본으로 하고, 성능 최적화가 필요한 경우에는 페치 조인(fetch join)을 사용해라!(V3에서 설명)
아무튼 ... 이 방법은 참고로만 알아놓자...