[JPA] Cascade 의 종류와 사용

HenryHong·2026년 1월 29일
post-thumbnail

1. Cascade가 뭐냐: 한 줄 정의

“부모 엔티티에 수행한 영속성 작업(저장, 삭제 등)을 연관된 자식 엔티티까지 전이시키는 옵션.”

  • 부모만 persist/remove 했는데 자식도 같이 persist/remove 되게 만드는 것.
  • 연관관계(@OneToMany, @ManyToOne…)에 cascade = ...로 설정한다.

2. CascadeType 종류 요약

JPA 표준 기준 주요 타입만 추리면:

  • PERSIST
    • 부모 persist 할 때 자식도 같이 영속화.
  • MERGE
    • 부모 merge 할 때 자식도 같이 병합.
  • REMOVE
    • 부모 remove 할 때 자식도 같이 삭제.
  • REFRESH
    • 부모 refresh 할 때 자식도 DB 상태로 새로고침.
  • DETACH
    • 부모 detach 할 때 자식도 준영속 상태로.
  • ALL
    • 위 전부 포함 (PERSIST, MERGE, REMOVE, REFRESH, DETACH).

3. 가장 많이 쓰는 패턴: 부모-자식 1:N

예시 도메인: 주문(Parent) - 주문상품(Child).

@Entity
public class Order {

    @Id @GeneratedValue
    private Long id;

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

    // 연관관계 편의 메서드
    public void addItem(OrderItem item) {
        orderItems.add(item);
        item.setOrder(this);
    }
}

@Entity
public class OrderItem {

    @Id @GeneratedValue
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    private Order order;

    // ...
}

어떻게 동작하나

  • CascadeType.PERSIST (ALL에 포함)

    Order order = new Order();
    OrderItem item1 = new OrderItem();
    OrderItem item2 = new OrderItem();
    
    order.addItem(item1);
    order.addItem(item2);
    
    em.persist(order); // Order만 persist

    → Order 뿐 아니라 두 개의 OrderItem도 자동으로 INSERT.

  • CascadeType.REMOVE + orphanRemoval = true

    em.remove(order);

    → Order 삭제 시 OrderItem 전부 DELETE.
    orphanRemoval = true 덕분에 order.getOrderItems().remove(item) 만 해도 해당 item이 고아가 되어 DB에서 같이 삭제.

블로그 포인트

“Cascade는 ‘연관관계를 맺은 엔티티 모두를 한 번에 다루고 싶을 때’ 쓰고,
특히 부모-자식처럼 생명주기가 강하게 묶인 관계에서 @OneToMany(cascade = ALL, orphanRemoval = true) 조합이 많이 쓰인다.”


4. 언제 쓰면 좋은지 / 언제 조심해야 하는지

쓰면 좋은 케이스

  • 부모와 자식이 거의 하나의 생명주기를 공유할 때

    • 예: 주문 ↔ 주문상품, 게시글 ↔ 댓글(완전히 종속적인 경우), 설문 ↔ 보기 항목.
    • 부모 없이 자식이 단독으로 존재할 일이 없는 구조.
  • 새 그래프 생성/저장 코드 단순화

    • em.persist(order); 한 줄로 order + orderItems 모두 저장 가능 → 서비스 코드가 깔끔해짐.

조심해야 할 케이스

  • CascadeType.ALL 남발

    • 예: @ManyToOne 쪽에 ALL 걸어두면, 특정 자식 삭제 시 부모까지 같이 삭제될 수 있음.
    • 서로 생명주기가 다르면 ALL 말고 필요한 것만 선택하는 게 안전.
  • 공유되는 엔티티

    • 카테고리, 상품, 회원처럼 여러 곳에서 참조하는 엔티티에 REMOVE cascade 걸면 매우 위험.
    • 이런 건 “참조만 끊고, 실제 삭제는 별도 서비스/배치에서 처리”가 보통.

블로그에 넣을 수 있는 한 줄:

“Cascade는 ‘내가 이 엔티티를 삭제/저장하면, 저 엔티티도 같이 그렇게 돼도 되는가?’를 스스로에게 물어보고,
그래프 전체의 생명주기가 진짜로 같이 움직일 때만 사용하는 것이 안전하다.”


5. orphanRemoval vs CascadeType.REMOVE

자주 헷갈리는 포인트라 이거 하나만 정리해두면 블로그 퀄리티 확 올라간다.

  • cascade = REMOVE

    • 부모를 remove() 할 때만 자식도 같이 삭제.
    • 단순히 컬렉션에서 빼기만 하면 DB에는 남아있음.
  • orphanRemoval = true

    • 부모 컬렉션에서 자식을 제거하거나, 연관관계를 끊으면
      자식 엔티티를 고아로 보고 DB에서 삭제.
    • 부모는 살아있고, 특정 자식만 삭제하는 케이스에 적합.

한 줄 비교:

cascade = REMOVE는 ‘부모 삭제 시 같이 삭제’,
orphanRemoval = true는 ‘부모는 살리고 자식만 컬렉션에서 빼면 삭제’.”


6. 마무리

  • Cascade는 JPA의 영속성 전이 기능으로, 부모 엔티티에 수행한 작업을 자식에게 자동으로 전파해 준다.
  • PERSIST, REMOVE, MERGE 등 필요한 타입만 선택하거나, 생명주기가 완전히 같이 가는 경우엔 ALL을 쓴다.
  • 부모-자식 1:N 에서는 cascade = ALL, orphanRemoval = true 조합을 많이 쓰지만,
    반대로 여러 곳에서 공유되는 엔티티에 REMOVE/ALL을 남발하면 데이터가 예상치 못하게 같이 삭제될 수 있으니 주의가 필요하다.
profile
주니어 백엔드 개발자

0개의 댓글