[스프링 활용2] 주문 조회 V1 : 엔티티 직접 노출 (비추)

atdawn·2024년 7월 10일

SPRING BOOT+JPA

목록 보기
39/49

참고 : [실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화] - 김영한


엔티티를 직접 노출하는 방법으로 주문 조회를 구현해보자.

해당 방법은 실무에서 절!대! 추천하지 않는다.

OrderSimpleApiController 생성

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; //연관관계 주인
  • Order클래스에서 Member을 가져오게 되는데 이때, fetch가 LAZY로 설정되어있다.
  • 지연로딩의 경우 DB에서 가져올때 Order의 데이터만 가져오고 Member의 데이터는 가져오지 않는다.
  • Hibernate에서는 연관된 엔티티를 지연 로딩(LAZY)으로 설정할 경우, 실제 데이터를 DB에서 가져오는 대신 프록시 객체를 생성하여 필요할 때 데이터를 초기화한다. 객체의 값을 꺼내야할 때, DB에 Member객체 SQL을 날려서 값을 채운다 : 프록시 초기화
  • 프록시 기술을 쓸때 bytebuddy라이브러리를 사용하게 되는데 제이슨이 객체를 가져올때, 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에서 설명)

아무튼 ... 이 방법은 참고로만 알아놓자...

profile
복습 복습 복습

0개의 댓글