[Spring] 양방향 매핑으로 인한 순환 참조

2024. 1. 15. 16:46Spring

 

엔티티 구조

 

Product_order Entity

@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class Product_order extends BaseEntity {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "product_order_id")
    private Long product_order_id;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private Users user;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "store_id")
    private Stores stores;

    @OneToMany(mappedBy = "product_order")
    private List<Product_orderItem> orderItems;
}

 

Product_orderItem

@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class Product_orderItem {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "product_orderItem_id")
    private Long product_orderItem_id;
    private String product_orderItem_color;
    private Float product_orderItem_size;
    private String product_orderItem_other;
    private int product_orderItem_quantity;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "product_order_id")
    private Product_order product_order;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "product_id")
    private Products products;
    
    }

 

구조

 

엔티티 구조를 살펴보면 Product_order에 Product_orderItem이 OneToMany 상태로 매핑되어 있는 것을 확인할 수 있다. 주문의 경우 여러개의 상품을 가질 수 있는 구조이기 때문입니다. 

 

발생하는 문제

  1. 무한 순환 참조 : Product_order의 경우 Product_orderItem의 리스트를 가지고 있고 Product_orderItem은 다시 Product_order를 참조합니다. 이 과정에서 api에서 엔티티를 참조, 반환하면 JSON으로 데이터를 직렬화 하는 과정에 무한 순환 참조가 발생하게 됩니다.

  2. 지연 로딩 오류 : FetchType 또한 Lazy로 설정되어 있어 관련 엔티티는 실제로 접근될 때까지 데이터베이스에서 로드되지 않습니다. API 응답으로 Entitiy를 직접 반환하면 지연로딩된 데이터를 호출할 때 LazyInitializationException 오류가 발생한다. 이 예외의 발생 원인은 대체로 지연 로딩된 데이터를 호출하는 시점에 이미 Hibernate 세션이나 JPA 트랜잭션이 종료되어 해당 데이터에 접근할 수 없는 경우가 발생한다.

  3. Entitiy 외부 노출 : Entitiy의 필드 형식이 외부로 노출되는 문제가 발생한다. 보안적인 문제와 불필요한 필드 노출 문제가 발생되고 마지막으로 Entitiy 필드의 형식이 변경되는 경우 데이터의 유연한 대응이 힘들어진다.

 

위에 3가지 문제를 한번에 해결 방법으로는 DTO(Data Transfer Object)를 이용해 데이터를 반환하면 순환 참조, 지연로딩, Entitiy 외부 노출 문제를 한번에 해결할 수 있다.

 

응답 방식

 

@PostMapping("/order")
    public ResponseEntity<ResponseDto> createOrder(@RequestHeader("user_id") Long user_id,
                                                   @RequestHeader("store_id") Long store_id,
                                                   @RequestBody ProductOrderDto productOrderDto) {
        productOrderService.createOrder(user_id, store_id, productOrderDto);
        return ResponseEntity.ok(new ResponseDto("제품 주문 완료"));
    }

 

기존 Entitiy 응답 방식에서 Dto를 이용해 데이터를 전달 호출하였다. 물론 반환 값을 줄 필요가 없다 생각해 Response 값은 productOrderDto가 아닌 성공 여부를 알려주는 응답 Dto로 대체 했다.