본문 바로가기

Spring&IntelliJ

cascade = CascadeType.ALL은 어떨때 주로 쓰이고 왜 쓰여?

cascade = CascadeType.ALL

의 의미:

부모 엔티티의 저장, 수정, 삭제 같은 상태 변화를 자식 엔티티에게 자동으로 전파한다.

주로 사용하는 경우:

✅ 부모와 자식의 생명주기가 동일할 때
✅ 자식이 부모 없이 의미가 없을 때

대표적인 예:

Order
 |
 ├── OrderItem
 |
 └── Delivery
 

같은 도메인입니다.

반대로:

Member
 |
Order
 

처럼 독립적으로 관리해야 하는 데이터에는 신중하게 사용해야 합니다.

cascade = CascadeType.ALL은 부모 엔티티의 상태 변화를 자식 엔티티에게 자동으로 전파하기 위해 사용합니다.

쉽게 말하면:

"부모를 저장하거나 삭제할 때, 연결된 자식도 같이 처리해줘"

라는 의미입니다.


1. Cascade가 필요한 상황

예를 들어 주문 시스템을 생각해보겠습니다.

 
@Entity
public class Order {

    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
    private List<OrderItem> orderItems = new ArrayList<>();
}
 

주문(Order)과 주문 상품(OrderItem)이 있습니다.

관계:

Order (주문)
 |
 | 1:N
 |
OrderItem (주문상품)
 

데이터:

Order
id = 1
주문자 = "kim"

OrderItem
id = 10
상품 = "노트북"

OrderItem
id = 11
상품 = "마우스"
 

2. Cascade가 없으면?

cascade가 없다면 저장할 때 이렇게 해야 합니다.

 
Order order = new Order();

OrderItem item1 = new OrderItem();
OrderItem item2 = new OrderItem();

order.addOrderItem(item1);
order.addOrderItem(item2);


orderRepository.save(order);

orderItemRepository.save(item1);
orderItemRepository.save(item2);
 

부모와 자식을 각각 저장해야 합니다.


3. CascadeType.ALL을 사용하면?

 
Order order = new Order();

OrderItem item1 = new OrderItem();
OrderItem item2 = new OrderItem();

order.addOrderItem(item1);
order.addOrderItem(item2);


orderRepository.save(order);
 

이 한 줄만 실행해도:

Order 저장
 ↓
OrderItem 저장
 ↓
OrderItem 저장
 

자동으로 전파됩니다.


CascadeType.ALL이 의미하는 것

ALL은 여러 cascade 옵션을 모두 포함합니다.

실제로는:

 
cascade = {
    CascadeType.PERSIST,
    CascadeType.MERGE,
    CascadeType.REMOVE,
    CascadeType.REFRESH,
    CascadeType.DETACH
}
 

와 같습니다.

대표적인 것만 보면:

옵션의미
PERSIST 부모 저장 시 자식도 저장
REMOVE 부모 삭제 시 자식도 삭제
MERGE 부모 수정 시 자식도 병합
REMOVE 삭제 전파

4. 실무에서 가장 많이 쓰는 경우

① 부모와 자식의 생명주기가 같은 경우

예:

주문(Order)
 |
 ├── 주문상품(OrderItem)
 ├── 배송(Delivery)
 

주문이 없으면 주문상품도 의미가 없습니다.

즉:

Order 생성
      ↓
OrderItem 생성

Order 삭제
      ↓
OrderItem 삭제
 

같이 움직여야 합니다.

그래서:

 
@OneToMany(
    mappedBy = "order",
    cascade = CascadeType.ALL
)
private List<OrderItem> orderItems;
 

를 사용합니다.


② 일대일 관계

예:

회원(Member)
 |
주소(Address)
 

회원만 존재하고 주소만 따로 존재할 필요가 없다면:

 
@OneToOne(
    cascade = CascadeType.ALL
)
private Address address;
 

사용 가능합니다.


하지만 모든 곳에 CascadeType.ALL을 쓰면 안 됩니다

예를 들어:

회원(Member)
 |
주문(Order)
 

관계에서:

 
@OneToMany(
    cascade = CascadeType.ALL
)
private List<Order> orders;
 

이렇게 하는 것은 위험합니다.

왜냐하면:

회원 삭제
 ↓
모든 주문 삭제
 

가 되어버립니다.

하지만 실제 서비스에서는 주문 데이터는 법적으로 보관해야 할 수도 있습니다.


Cascade와 orphanRemoval 차이

둘은 자주 같이 헷갈립니다.

Cascade

부모 자체가 변경될 때 전파

예:

 
orderRepository.delete(order);
 

결과:

Order 삭제
 ↓
OrderItem 삭제
 

orphanRemoval

컬렉션에서 제거된 자식을 삭제

예:

 
order.getOrderItems().remove(item);
 

결과:

OrderItem이 컬렉션에서 빠짐
 ↓
DB에서도 삭제