인프런 커뮤니티 질문&답변

whrbdnjs33님의 프로필 이미지
whrbdnjs33

작성한 질문수

실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발

주문 기능 테스트

주문취소와 영속성 컨텍스트

해결된 질문

작성

·

337

0

1. 테스트의 주문취소 부분에서,  멤버가 가지고 있는 order에도 주문취소가 적용되는 지 확인하고 싶어

assertEquals(OrderStatus.CANCEL, member.getOrders().get(0).getStatus());

이렇게 코드를 추가해보니 테스트가 통과되더라구요. 이것도 영속성컨텍스트가 다 관리해주기 때문인가요??

 

2. 혹시나 하는 호기심에 cancelOrder대신에 em.remove 를 사용하여 해당 Order를 지워봤습니다.  그렇게 할 경우 db내에서 해당 튜플은 지워지지만 위에처럼 객체 내에서 추적하여 Member가 가지고 있는 Order에 영향을 주는가했더니 그렇지 않더라구요. 이런식으로 작동하는것이 맞나요? 아니면 제가 놓치고 있는것이 있을까요.

 

3. (2번이 맞다는가정의 질문)  앞으로 다른 앱을 만들 때 게시글이라던지 특정 객체를 삭제하고 싶을 때, 영속성 컨텍스트가 관리를 해주기 원한다면, 이런식으로 일종의 논리삭제를 해주는 식으로 진행해야 하는건가요?

답변 1

0

안녕하세요. whrbdnjs33님, 공식 서포터즈 David입니다.

.
1. 영속성 컨텍스트와 무관하게

orderService.cancelOrder()는 내부에서 order.cancel() 메서드를 호출합니다.

order.cancel() 메서드는 status를 CANCEL로 바꾸는 로직을 가지고 있습니다.

따라서 주문취소가 발생하면 로직상 OrderStatus가 CANCEL로 되는 것입니다.

 

2. 이런 방식을 soft delete라고 합니다. DB에서 주문 데이터 자체를 삭제하는 것이 아니라 논리적으로 삭제처리하는 것입니다. 취소된 내역이라 할지라도 원본 데이터는 보존하여 문제 발생시 데이터를 추적할 때 사용될 수도 있습니다.

 

3. 물리적으로 삭제를 원하신다면 OrderService에서 DB에서 데이터를 삭제하는 메서드를 호출하시면 됩니다. 논리삭제와는 무관합니다.
.
감사합니다.

whrbdnjs33님의 프로필 이미지
whrbdnjs33
질문자

아 제가 물어보고자 했던 부분은 단순히 order의 주문상태가 변경됐다는 것이 아니라, member가 가지고 있는 order객체의 주문상태도 변경이 됐다는 부분입니다.

두 코드의 차이점에 유의해주세요.

원래코드: 

assertEquals(OrderStatus.CANCEL, order.getStatus());

추가한 코드: 

assertEquals(OrderStatus.CANCEL, member.getOrders().get(0).getStatus());

 

그런데 제가 2번에서 의아했던 부분은, 물리삭제를 하면 위의 경우처럼 member가 가지고 있는 order객체가 사라지는 것이 아니라, 여전히 살아있는 부분 이었습니다.

그래서 논리삭제(상태변경)를 하면 영속성컨텍스트가 관리해주지만, 물리삭제를 하면 그렇게 해주지 않는건가? 라고 생각했던 것입니다.

 

혹시나 해서 제가 돌렸던 테스트코드도 아래 첨부하겠습니다.

@Test
public void 주문취소() throws Exception {
//given
Member member = createMember();
Item item = createBook("책이름1", 10000, 10);

int orderCount = 2;

Long orderId = orderService.order(member.getId(), item.getId(), orderCount);

//when
orderService.cancelOrder(orderId);

//then
Order order = orderRepository.findOne(orderId);
assertEquals(OrderStatus.CANCEL, order.getStatus());
assertEquals(10, item.getStockQuantity());

assertEquals(OrderStatus.CANCEL, member.getOrders().get(0).getStatus(), "따로 추가. member가 가지고 있는 order도 변경된다.");
}

@Test
public void 주문삭제() throws Exception {
//given
Member member = createMember();
Item item = createBook("책이름1", 10000, 10);

int orderCount = 2;

Long orderId = orderService.order(member.getId(), item.getId(), orderCount);

//when
orderService.deleteOrder(orderId);
Member member1 = memberService.findOne(member.getId());
Order order = orderRepository.findOne(orderId);

//then
assertEquals(OrderStatus.ORDER, member1.getOrders().get(0).getStatus(), "통과되면 안될텐데 통과된다");
assertEquals(null, order, "order 튜플은 삭제된게 맞음");
}
말씀해주신대로 제가 놓친 부분이 있었네요.
 
2번에서 물리적으로 삭제는 진행되었지만 왜 Member는 주문을 가지고 있느냐에 대해서 물으신걸로 이해하고 답변드리겠습니다.
 
물리적으로 주문을 삭제한 다음, 멤버를 다시 DB로부터 불러오기 전까지는 멤버 객체의 주문목록에는 삭제된 주문 객체가 그대로 있습니다.
주문객체를 추가하는 것과 같이 제거하는 로직을 별도로 만들어 실행하거나
영속성 컨텍스트를 초기화한 다음 다시 멤버 엔티티를 불러오는 방법을 사용하시면 됩니다.
저는 후자를 사용했고 아래 보시면 초기화한 다음 다시 멤버 정보를 불러오면 주문 내역이 비어있는 것을 볼 수 있습니다.

whrbdnjs33님의 프로필 이미지
whrbdnjs33
질문자

아하 em.flush()와 em.clear()가 필요했군요!!

감사합니다! 이상했던 부분이 해결됐어요.

근데 알림메일에선 글이 보였는데 여기선 (작성중) 이라고 나오면서 글이 안보이네요ㅠㅠ

수정이 필요하다고 생각해서 잠깐 변경해두었습니다. 다시 올렸어요:)

whrbdnjs33님의 프로필 이미지
whrbdnjs33
질문자

감사합니다!

whrbdnjs33님의 프로필 이미지
whrbdnjs33

작성한 질문수

질문하기