Order에서 createOrder를 생성하면서 서비스 로직에서
request에는 원하는 메뉴Id와 수량을 적는 OrderMenu를 json형식으로 받아온다.
그 이후에 거기서 Order 객체를 생성하여 Repository를 통해서 저장하니 로직은 돌아갔는데
여기서 생기는 의문점은 그럼 OrderMenu는 저장을 안하나? 그럼 나중에 조회해올 때 내가 주문한 메뉴, 가격 등을 알 수 없나였다. 역시나 Order 주문 내역을 조회하는 로직을 구현할 때 OrderMenu는 가져오지못하고 빈 리스트 []를 가져오게 되어 createOrder부분에서 request로 들어오는 OrderMenu 리스트를 저장해서 가져오자가 문제 배경이다.
OrderMenu
리스트는 request로 JSON으로 받아왔지만,Order
만 생성하고 저장(orderRepository.save(order)
)OrderMenu
는 저장 안 되는 것처럼 보임 → 실제로도 order.getOrderMenus()
는 [] 빈 리스트가 나옴List<OrderMenu> orderMenuList = requestDto.getMenus().stream().map(menuDto ->{
Menu menu = menuRepository.findMenuByIdOrElseThrow(menuDto.getMenuId());
// OrderMenu 객체를 생성하고, 수량 설정
OrderMenu orderMenu = new OrderMenu();
orderMenu.setMenu(menu); // Menu 객체 설정
orderMenu.setQuantity(menuDto.getQuantity()); // 수량 설정
return orderMenu;
}).collect(Collectors.toList());
OrderService에서 requestDto에 들어오는 OrderMenu를 리스트로 저장하기 위해 orderMenuList를 생성한다. requestDto.getMenus()로 메뉴 정보 리스트를 가져온다. stream을 이용하여 menuId로 Menu를 조회한다. 그 뒤에 OrderMenu 객체에 조회한 Menu 객체를 설정한다.
그리고 요청받은 수량 정보를 setQuantity()를 통해 설정해준다.
생성된 OrderMenu객체(메뉴랑 수량)들을 collect(Collectors.toList())를 통해 리스트 형태로 저장한다.
Order order = new Order(totalPrice, generateOrderNumber(), loginUser, orderMenuList)
Order createdOrder = orderRepository.save(order);
public Order (Long totalPrice,String orderNumber,User user, List<OrderMenu> orderMenus) {
this.orderMenus = orderMenus;
this.totalPrice = totalPrice;
this.user = user;
this.orderNumber = orderNumber;
this.orderStatus = OrderStatus.REQUESTED;
}
그 뒤로 Order 객체를 새로 생성하여 총 가격, 주문번호, 로그인한 유저 정보, 오더메뉴 리스트를 주입해주었다. 주입한 것을 저장해주고 반환해주었다.
그 뒤에 PostMan으로 실행했으나 orderId가 생성되지 않아서 오류가 생성.
그래서 열심히하다가 튜터님께 찾아뵙다.
// Order.java
public Order (Long totalPrice,String orderNumber,User user, List<OrderMenu> orderMenus) {
for(OrderMenu orderMenu : orderMenus){
orderMenu.matchOrder(this);
}
this.orderMenus.addAll(orderMenus);
this.totalPrice = totalPrice;
this.user = user;
this.orderNumber = orderNumber;
this.orderStatus = OrderStatus.REQUESTED;
}
// OrderMenu.java
public void matchOrder(Order order) {
this.order = order;
}
Order 생성자에 OrderMenu 객체를 리스트로 받아오긴 했지만 각 OrderMenu의 order 필드는 null 상태로 남아있다. 즉, 양방향 연관관계 중 OrderMenu -> Order 방향이 끊긴 상태
Order 생성자에서 OrderMenu 리스트를 받아올 때, 각 OrderMenu 객체에 order를 설정해주어야 한다. 이를 위해 matchOrder()메소드를 OrderMenu 클래스에 추가하여 Order를 create할 때 새로 생성하는 Order 생성자를 넣어준다.
Order 생성자에서 orderMenus 리스트를 반복하면서 matchOrder(this)를 호출해, 각 OrderMenu 객체의 order 필드를 현재 Order로 설정
// Json
{
"orderNumber": "DWS0425125527-978107",
"orderMenus": [
{
"id": 1,
"order": {
"createdAt": "2025-04-25T12:55:27.628749",
"updatedAt": "2025-04-25T12:55:27.628749",
"id": 1,
"orderNumber": "DWS0425125527-978107",
"totalPrice": 82000,
"orderStatus": "REQUESTED",
"orderMenus": [
{
"id": 1,
"order": {
"createdAt": "2025-04-25T12:55:27.628749",
"updatedAt": "2025-04-25T12:55:27.628749",
"id": 1,
"orderNumber": "DWS0425125527-978107",
"totalPrice": 82000,
"orderStatus": "REQUESTED",
"orderMenus": [
OrderMenu가 잘 출력은 되지만 중복되며 반복되는 문제가 발생.
이 문제는 양방향 연관관계 때문에 발생한다고 한다.
Order 객체를 직렬화 할 때 Order가 OrderMenu 객체들을 포함하고, 또 그 OrderMenu 객체들이 다시 Order를 참조하는 구조로 인해 순환참조가 일어나게 되어서 발생함
서로서로 양방향이라 서로 조회하다보니 발생하는 문제라 이걸 DTO를 추가하여 순환을막고 필요한 정보만 직렬화함.
@Getter
public class OrderMenuResponseDto {
private final Long id;
private final String menuName;
private final int quantity;
public OrderMenuResponseDto(OrderMenu orderMenu) {
this.id = orderMenu.getId();
this.menuName = orderMenu.getMenu().getName();
this.quantity = orderMenu.getQuantity();
}
}
응답 전용 DTO를 생성.
@Getter
public class CreatedOrderResponseDto {
private final String orderNumber;
private final List<OrderMenuResponseDto> orderMenus;
private final Long totalPrice;
private final LocalDateTime createdAt;
public CreatedOrderResponseDto(Order order) {
this.orderNumber = order.getOrderNumber();
this.totalPrice = order.getTotalPrice();
this.createdAt = order.getCreatedAt();
this.orderMenus = order.getOrderMenus().stream()
.map(OrderMenuResponseDto::new)
.collect(Collectors.toList());
}
}
CreateOrderResposeDto를 수정
this.orderMenus = order.getOrderMenus().stream().map(OrderMenuResponseDto::new).collect(Collectors.toList());
여기서 OrderMenu 객체들을 그대로 클라이언트로 반환하는 것이 아니라, OrderMenuResponseDto를 만든것으로 반환.
Order와 OrderMenu는 양방향 연관관계를 갖고 있기 때문에, OrderMenu 객체가 Order를 참조하고, Order 객체가 또 OrderMenu를 참조하는 구조이기에 무한 순환이 일어났으니 이걸 방지하기위해서 OrderMenuResponseDto라는 중간다리가 양방향 관계를 끊어주면서 직렬화를 만들어준다.
return new CreatedOrderResponseDto(createdOrder);
그 뒤에 반환을 createdOrder로 해주면 순환하지않고 반환해준다.
기존 방식: Order -> OrderMenu -> Order -> OrderMenu … 순환 참조 발생 가능
DTO 변환 방식: Order -> OrderMenuResponseDto -> 필요한 정보만 포함된 OrderMenu 객체들 반환
완성!!! 정말 오래 헤맸지만 연관관계가 이렇게 중요한지 몰랐다.
연관관계에 따라서 양방향 관리를 잘 해줘야겠다고 생각했다.