๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐ŸŒท Spring/๊ฐœ๋…

[Spring][JPA] Cascade์˜ ์˜๋ฏธ์™€ ์ข…๋ฅ˜ ์•Œ์•„๋ณด๊ธฐ.

by hyeong._.ing 2026. 6. 9.

 

 

๋‹ค๋Œ€๋‹ค ๋งคํ•‘ ์—”ํ‹ฐํ‹ฐ ๊ตฌ์กฐ๋ฅผ ์„ค๊ณ„ํ•˜๋ฉด์„œ
Cascade ํƒ€์ž…์„ ์„ค์ •ํ–ˆ๋‹ค.
Cascade๊ฐ€ ๋ฌด์—‡์ด๋ฉฐ ์–ด๋–ค ์ข…๋ฅ˜๊ฐ€ ์žˆ๋Š”์ง€ ์‚ดํŽด๋ณด์ž.

 

 

 

 

1. Cascade

  • ์˜์†์„ฑ์€ ์ƒ์„ฑ๋œ ๋ฐ์ดํ„ฐ๋‚˜ ๊ฐ์ฒด๊ฐ€ ํ”„๋กœ๊ทธ๋žจ์„ ์ข…๋ฃŒํ•˜๊ฑฐ๋‚˜ ์‹œ์Šคํ…œ์ด ์žฌ๋ถ€ํŒ…๋˜์–ด๋„ ์‚ฌ๋ผ์ง€์ง€ ์•Š๊ณ  ์˜๊ตฌ์ ์œผ๋กœ ์ง€์†๋˜๋Š” ํŠน์„ฑ์„ ๋งํ•œ๋‹ค.
  • Cascade๋Š” ์˜์†์„ฑ ์ „์ด๋กœ ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์ˆ˜ํ–‰ํ•œ ์˜์†์„ฑ ์ž‘์—…์„ ์—ฐ๊ด€๋œ ์ž์‹ ์—”ํ‹ฐํ‹ฐ์—๊ฒŒ๋„ ์ž๋™์œผ๋กœ ์ „๋‹ฌํ•˜๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.
  • ์˜ˆ๋ฅผ ๋“ค์–ด ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ €์žฅํ•  ๋•Œ ์ž์‹ ์—”ํ‹ฐํ‹ฐ๋„ ํ•จ๊ป˜ ์ €์žฅํ•˜๊ฑฐ๋‚˜ ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์‚ญ์ œํ•  ๋•Œ ์ž์‹ ์—”ํ‹ฐํ‹ฐ๋„ ํ•จ๊ป˜ ์‚ญ์ œ๋˜๋„๋ก ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.
  • JPA์—์„œ๋Š” ์ด๋Ÿฌํ•œ ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๊ฐ„์˜ ์˜์กด์„ฑ์„ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด Enum ํƒ€์ž…์˜ jakarta.persistence.CascadeType์„ ์ œ๊ณตํ•˜๊ณ  ์žˆ๋‹ค. JPA์—์„œ CascadeType์„ ํ†ตํ•ด ์–ด๋–ค ์ž‘์—…์„ ์ž์‹ ์—”ํ‹ฐํ‹ฐ์—๊ฒŒ ์ „์ดํ• ์ง€ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๋ง์ด๋‹ค.
  • ์ฃผ์˜) ์ž์‹ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ๋ถ€๋ชจ ํ•˜๋‚˜์—๊ฒŒ๋งŒ ์†Œ์œ ๋˜๋Š” ๊ตฌ์กฐ์ผ ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์•ˆ์ „ํ•˜๋‹ค. ๋งŒ์•ฝ ์—ฌ๋Ÿฌ ๋ถ€๋ชจ์—๊ฒŒ์„œ ๊ณต์œ ๋˜๋Š” ์—”ํ‹ฐํ‹ฐ์— REMOVE ๊ฐ™์€ Cascade๋ฅผ ๊ฑธ๋ฉด, ์˜๋„ํ•˜์ง€ ์•Š์€ ๋ฐ์ดํ„ฐ๊นŒ์ง€ ํ•จ๊ป˜ ์‚ญ์ œ๋  ์ˆ˜ ์žˆ๋‹ค.

 

 

 


 

 

 

 

2. CascadeType

  • CascadeType.ALL
    ์•„๋ž˜์˜ ๋ชจ๋“  Cascade ์„ค์ •์„ ํ•œ ๋ฒˆ์— ์ ์šฉํ•œ๋‹ค.

  • CascadeType.PERSIST
    ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์˜์†ํ™”๋  ๋•Œ, ์—ฐ๊ด€๋œ ์ž์‹ ์—”ํ‹ฐํ‹ฐ๋„ ํ•จ๊ป˜ ์˜์†ํ™”๋œ๋‹ค.

  • CascadeType.REMOVE
    ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์‚ญ์ œ๋  ๋•Œ, ์—ฐ๊ด€๋œ ์ž์‹ ์—”ํ‹ฐํ‹ฐ๋„ ํ•จ๊ป˜ ์‚ญ์ œ๋œ๋‹ค.

  • CascadeType.MERGE
    ์ค€์˜์† ์ƒํƒœ์˜ ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ๋ณ‘ํ•ฉ๋  ๋•Œ, ์ž์‹ ์—”ํ‹ฐํ‹ฐ๋„ ํ•จ๊ป˜ ๋ณ‘ํ•ฉ๋œ๋‹ค.

  • CascadeType.REFRESH
    ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ๊ฐ€ DB๋กœ๋ถ€ํ„ฐ ๊ฐ’์„ ๋‹ค์‹œ ์ฝ์–ด์˜ฌ ๋•Œ, ์ž์‹ ์—”ํ‹ฐํ‹ฐ๋„ ํ•จ๊ป˜ ์ƒˆ๋กœ๊ณ ์นจํ•œ๋‹ค.

  • CascadeType.DETACH
    ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ ๋ถ„๋ฆฌ(์ค€์˜์†ํ™”)๋  ๋•Œ, ์ž์‹ ์—”ํ‹ฐํ‹ฐ๋„ ํ•จ๊ป˜ ๋ถ„๋ฆฌ๋œ๋‹ค.

 

 

 


 

 

 

 

3. Cascade ์‚ฌ์šฉ์˜ ๋Œ€ํ‘œ์ ์ธ ์˜ˆ์‹œ
    ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์€ ๋ˆ„๊ตฌ์ผ๊นŒ?

  • Order๊ณผ OrderItem
Order 1๊ฐœ
 โ””โ”€โ”€ OrderItem ์—ฌ๋Ÿฌ ๊ฐœ
์ฃผ๋ฌธ ํ•˜๋‚˜ ์•ˆ์— ์—ฌ๋Ÿฌ ์ฃผ๋ฌธ์ƒํ’ˆ์ด ๋“ค์–ด๊ฐ„๋‹ค.

 

Order
 โ”œโ”€โ”€ OrderItem: ์‚ฌ๊ณผ
 โ”œโ”€โ”€ OrderItem: ์šฐ์œ 
 โ””โ”€โ”€ OrderItem: ๊ณผ์ž
์‚ฌ์šฉ์ž๊ฐ€ ์‚ฌ๊ณผ, ์šฐ์œ , ๊ณผ์ž๋ฅผ ์ฃผ๋ฌธํ–ˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž.

 

์ฃผ๋ฌธ ์—”ํ‹ฐํ‹ฐ

@Entity
public class Order {

    // Order๋ฅผ ์ €์žฅํ•  ๋•Œ, OrderItem๋„ ํ•จ๊ป˜ ์ €์žฅ๋œ๋‹ค.
    @OneToMany(mappedBy = "order", cascade = CascadeType.PERSIST)
    private List<OrderItem> orderItems = new ArrayList<>();

    // ์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€๊ด€๊ณ„ ํŽธ์˜ ๋ฉ”์„œ๋“œ
    // ๋ถ€๋ชจ์™€ ์ž์‹์˜ ๊ด€๊ณ„๋ฅผ ํ•œ ๋ฒˆ์— ๋งž์ถฐ์ค€๋‹ค.
    public void addOrderItem(OrderItem orderItem) {
        orderItems.add(orderItem);
        orderItem.setOrder(this);
    }
}
@OneToMany(mappedBy = "order", cascade = CascadeType.PERSIST)

 

ํ•˜๋‚˜์˜ ์ฃผ๋ฌธ์—๋Š” ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ฃผ๋ฌธ ์•„์ดํ…œ์ด ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ Order ์—”ํ‹ฐํ‹ฐ์—์„œ๋Š” @OneToMany๋ฅผ ์‚ฌ์šฉํ•ด OrderItem ๋ชฉ๋ก์„ ๊ฐ€์ง„๋‹ค.

๋จผ์ € ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ๋Š” Order, ์ž์‹ ์—”ํ‹ฐํ‹ฐ๋Š” OrderItem์ด ๋งž๋‹ค. ์ฃผ๋ฌธ์ด ์žˆ์–ด์•ผ ์ฃผ๋ฌธ ์•„์ดํ…œ์ด ์˜๋ฏธ๋ฅผ ๊ฐ€์ง€๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์‚ฌ๊ณผ, ์šฐ์œ , ๊ณผ์ž๋ฅผ ์ฃผ๋ฌธํ–ˆ๋‹ค๋ฉด ์ด ์ƒํ’ˆ๋“ค์€ ๋ชจ๋‘ ํ•˜๋‚˜์˜ ์ฃผ๋ฌธ ์•ˆ์— ํฌํ•จ๋œ๋‹ค. ๊ทธ๋ž˜์„œ ๊ฐœ๋…์ ์œผ๋กœ Order๊ฐ€ ๋ถ€๋ชจ์ด๊ณ , OrderItem์ด ์ž์‹์ด๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ์—ฌ๊ธฐ์„œ mappedBy = "order"๋Š” ์ด ๊ด€๊ณ„์˜ ์ฃผ์ธ์€ order๊ฐ€ ์•„๋‹Œ OrderItem ์—”ํ‹ฐํ‹ฐ์˜ order ํ•„๋“œ๋ผ๊ณ  ์•Œ๋ฆฌ๊ณ  ์žˆ๋‹ค. ์ด๊ฒƒ์€ ๋˜ ๋ฌด์Šจ ์ด์•ผ๊ธฐ์ผ๊นŒ?

JPA์—์„œ ๋งํ•˜๋Š” ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์€ ๋ถ€๋ชจ์™€ ์ž์‹์„ ๋”ฐ์ง€๋Š” ๊ฐœ๋…์ด ์•„๋‹ˆ๋ผ, ์™ธ๋ž˜ ํ‚ค๋ฅผ ์‹ค์ œ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ์ชฝ์„ ๋งํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋‹ˆ๊นŒ ์˜์†์„ฑ ์ „์ด๋กœ ๋ดค์„๋•Œ๋Š” ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ๊ฐ€ Order์ธ๊ฒŒ ๋งž๊ณ  ๊ทธ๊ฑฐ์™€ ๋ณ„๊ฐœ๋กœ ์™ธ๋ž˜ํ‚ค๋ฅผ ๊ฐ€์ง€๋Š” ์ชฝ์€ OrderItem์ด๋ผ๊ณ  ๋งํ•˜๋Š” ๊ฒƒ์ด๋‹ค. mappedBy๋Š” ์ด ์™ธ๋ž˜ํ‚ค๋ฅผ ๋ˆ„๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋Š”์ง€ ์ •์˜ํ•œ๋‹ค.

 

 

์ฃผ๋ฌธ ์•„์ดํ…œ ์—”ํ‹ฐํ‹ฐ

@Entity
public class OrderItem {

    // OrderItem ์ž…์žฅ์—์„œ๋Š” ์ž์‹ ์ด ์–ด๋–ค Order์— ์†ํ•˜๋Š”์ง€ ์•Œ์•„์•ผ ํ•œ๋‹ค.
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "order_id")
    private Order order;

}
ํ…Œ์ด๋ธ”์„ ์—ด๊ณ  ๋ณด๋ฉด ๋” ์‰ฝ๊ฒŒ ์•Œ ์ˆ˜ ์žˆ๋‹ค. order ํ…Œ์ด๋ธ” ์•ˆ์— orderItem ๋ชฉ๋ก์ด ์ €์žฅ๋˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ๋Š” order_item ํ…Œ์ด๋ธ” ์•ˆ์— order_id๋ผ๋Š” ์™ธ๋ž˜ํ‚ค๊ฐ€ ๋“ค์–ด๊ฐ„๋‹ค. ์ฆ‰, ์–ด๋–ค ์ฃผ๋ฌธ ์•„์ดํ…œ์ด ์–ด๋–ค ์ฃผ๋ฌธ์— ์†ํ•˜๋Š”์ง€๋Š” OrderItem ํ…Œ์ด๋ธ”์˜ order_id ๊ฐ’์œผ๋กœ ์ •ํ•ด์ง„๋‹ค. ๊ทธ๋ž˜์„œ JPA์—์„œ๋Š” ์™ธ๋ž˜ํ‚ค๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” OrderItem์ด ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์ด ๋œ๋‹ค.

๋‹ค์‹œ ์ •๋ฆฌํ•˜๋ฉด ์ด๋ ‡๋‹ค. 

๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ Order
์ž์‹ ์—”ํ‹ฐํ‹ฐ OrderItem
์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ OrderItem
( order_item ํ…Œ์ด๋ธ”์ด order_id ์™ธ๋ž˜ ํ‚ค๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ )
Order๊ฐ€ ๋ถ€๋ชจ ์—”ํ‹ฐํ‹ฐ๋ผ๊ณ  ํ•ด์„œ ๋ฐ˜๋“œ์‹œ ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์ด ๋˜์ง€ ์•Š๋Š”๋‹ค. JPA์—์„œ ์—ฐ๊ด€๊ด€๊ณ„์˜ ์ฃผ์ธ์€ ํ•ญ์ƒ ์™ธ๋ž˜ํ‚ค๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์ชฝ์ด๊ณ  ์ด ์˜ˆ์‹œ์—์„œ๋Š” OrderItem์ด ๊ทธ ์—ญํ• ์„ ํ•œ๋‹ค.
์„œ๋น„์Šค๋กœ์ง

@Service
@RequiredArgsConstructor
public class OrderService {

    private final OrderRepository orderRepository;

    @Transactional
    public void createOrder() {
        Order order = new Order();

        OrderItem apple = new OrderItem("์‚ฌ๊ณผ");
        OrderItem milk = new OrderItem("์šฐ์œ ");

        // Order์™€ OrderItem์„ ์—ฐ๊ฒฐํ•œ๋‹ค.
        order.addOrderItem(apple);
        order.addOrderItem(milk);

        // CascadeType.PERSIST ๋•๋ถ„์— Order๋งŒ ์ €์žฅํ•ด๋„
        // ์—ฐ๊ฒฐ๋œ OrderItem๋“ค๋„ ํ•จ๊ป˜ ์ €์žฅ๋œ๋‹ค.
        orderRepository.save(order);
    }
}
    @OneToMany(mappedBy = "order", cascade = CascadeType.PERSIST)
    private List<OrderItem> orderItems = new ArrayList<>();


JPA๋Š” ์ด ์ฝ”๋“œ๋ฅผ ๋ณด๊ณ  Order๋ฅผ ์ €์žฅํ•  ๋•Œ, Order ์•ˆ์— ๋“ค์–ด์žˆ๋Š” orderItems๋„ ๊ฐ™์ด ์ €์žฅํ•œ๋‹ค. cascade = CascadeType.PERSIST๊ฐ€ Order ์—”ํ‹ฐํ‹ฐ ์•ˆ์˜ orderItems ํ•„๋“œ์— ๋ถ™์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, Order์—์„œ OrderItem ๋ฐฉํ–ฅ์œผ๋กœ ์ €์žฅ ์ž‘์—…์ด ์ „์ด๋œ๋‹ค. 

        // Order์™€ OrderItem์„ ์—ฐ๊ฒฐํ•œ๋‹ค.
        order.addOrderItem(apple);
        order.addOrderItem(milk);


์ด ๋ฉ”์„œ๋“œ์˜ ๋‚ด๋ถ€๋Š” ์ด๋ ‡๊ฒŒ ์ฒ˜๋ฆฌ๋œ๋‹ค.

public void addOrderItem(OrderItem orderItem) {
    // Order ์•ˆ์˜ ์ฃผ๋ฌธ ์•„์ดํ…œ ๋ชฉ๋ก์— ์ถ”๊ฐ€
    this.orderItems.add(orderItem);

    // OrderItem ์ž…์žฅ์—์„œ๋„ ์ž์‹ ์ด ์–ด๋–ค Order์— ์†ํ•˜๋Š”์ง€ ์ €์žฅ
    orderItem.setOrder(this);
}


๊ทธ๋Ÿฌ๋ฉด ๊ฐ์ฒด ์ƒํƒœ๋Š” ์ด๋ ‡๊ฒŒ ๋œ๋‹ค.

Order
 โ””โ”€โ”€ orderItems
      โ”œโ”€โ”€ apple  → order ์ฐธ์กฐ
      โ””โ”€โ”€ milk   → order ์ฐธ์กฐ


์ด ์ƒํƒœ์—์„œ

orderRepository.save(order);


JPA๋Š” Order๋ฅผ ๋จผ์ € ์ €์žฅํ•œ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ Order ์•ˆ์—๋Š” orderItems ํ•„๋“œ์— cascade = CascadeType.PERSIST ์„ค์ •์ด ์žˆ์—ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ์ด๊ฑธ ๋ณธ JPA๋Š” orderItems ์•ˆ์— ์žˆ๋Š” OrderItems๋“ค๋„ ๊ฐ™์ด ์ €์žฅํ•˜๋ คํ•œ๋‹ค. ๋‹ค์‹œ ์‰ฝ๊ฒŒ ์„ค๋ช…ํ•˜์ž๋ฉด OrderRepository.save(order) ํ•œ์ค„๋งŒ ์ž‘์„ฑํ•˜๋”๋ผ๋„ ๋‚ด๋ถ€์—์„  order ์ €์žฅ - apple ์ €์žฅ - milk ์ €์žฅ์ด ๋œ๋‹ค.

 

 

Q. ๋งŒ์•ฝ Cascade๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ?

A. Cascade๊ฐ€ ์—†๋‹ค๋ฉด ๋‹น์—ฐํžˆ JPA๋Š” Order๋งŒ ์ €์žฅํ•œ๋‹ค. orderRepository.save(order)๋ฅผ ์ ์œผ๋ฉด OrderItem์€ ์ž๋™ ์ €์žฅ๋˜์ง€ ์•Š๋Š”๋‹ค. ๊ทธ๋ž˜์„œ ์ง์ ‘ ์ €์žฅํ•ด์ค˜์•ผํ•œ๋‹ค.
orderRepository.save(order);
orderItemRepository.save(apple);
orderItemRepository.save(milk);