포스트

@OnDelete vs JPA Cascade: 영속성 컨텍스트 일관성 문제

@OnDelete vs JPA Cascade: 영속성 컨텍스트 일관성 문제

문제 상황

카카오테크캠퍼스에서 선물하기 API를 JPA로 마이그레이션하다가 상품 삭제 시 옵션도 함께 삭제해야 하는 요구사항이 있었다.

그런데 엔티티 구조가 단방향 참조였다.

1
2
3
4
5
@Entity
public class Option {
    @ManyToOne(fetch = FetchType.LAZY)
    private Product product;  // Option → Product 방향만 참조
}

Product 엔티티에는 Option에 대한 참조가 없었다. 이 상황에서 상품을 삭제할 때 연관된 옵션들도 삭제하려면 어떻게 해야 할까?


영속성 컨텍스트란?

JPA에서 가장 중요한 개념 중 하나다. 영속성 컨텍스트는 엔티티를 영구 저장하는 환경으로, 애플리케이션과 DB 사이에서 엔티티를 관리하는 1차 캐시 역할을 한다.

핵심 기능:

  • 1차 캐시: 같은 트랜잭션 내에서 동일한 엔티티를 조회하면 DB가 아닌 캐시에서 가져온다
  • 변경 감지 (Dirty Checking): 엔티티 변경 사항을 자동으로 감지해서 트랜잭션 커밋 시 DB에 반영한다
  • 동일성 보장: 같은 트랜잭션 내에서 같은 PK로 조회한 엔티티는 항상 같은 인스턴스다

해결책 1: @OnDelete (Hibernate)

Hibernate에서 제공하는 @OnDelete 어노테이션을 사용했다.

1
2
3
@ManyToOne(fetch = FetchType.LAZY)
@OnDelete(action = OnDeleteAction.CASCADE)
private Product product;

이렇게 하면 DB 레벨에서 ON DELETE CASCADE가 설정되어서, 상품이 삭제되면 연관된 옵션도 자동으로 삭제된다.


멘토님 피드백

그런데 멘토님한테 이런 피드백을 받았다.

“@OnDelete는 Hibernate 전용 기능이며, DB가 직접 cascade delete를 실행하기 때문에 JPA의 영속성 컨텍스트에 반영되지 않습니다. 즉, JPA의 일관성과 충돌 가능성이 있으므로 JPA cascade 방식이 더 바람직할 것 같아요.”

문제점:

  • @OnDelete는 DB가 직접 처리한다
  • JPA의 영속성 컨텍스트는 이 사실을 모른다
  • 같은 트랜잭션에서 삭제된 엔티티에 접근하면 예상치 못한 동작이 발생할 수 있다

예를 들어, @OnDelete로 Option이 DB에서 삭제됐는데, 영속성 컨텍스트에는 여전히 Option이 남아 있어서 마치 존재하는 것처럼 동작할 수 있다.


해결책 2: JPA Cascade (양방향 관계 필요)

JPA의 cascade를 쓰려면 양방향 관계가 필요하다.

1
2
3
4
5
@Entity
public class Product {
    @OneToMany(mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Option> options = new ArrayList<>();
}

이렇게 하면 JPA가 삭제를 관리해서 영속성 컨텍스트와 일관성을 유지할 수 있다. Product를 삭제할 때 JPA가 연관된 Option들도 영속성 컨텍스트에서 함께 제거한다.


트레이드오프

방법장점단점
@OnDelete단방향 유지 가능, 설정 간단JPA 영속성 컨텍스트와 불일치
JPA Cascade영속성 컨텍스트 일관성 보장양방향 관계 필요

결국 양방향 관계를 추가해서 JPA Cascade를 쓰는 방향으로 수정했다.


배운 점

  • Hibernate 전용 기능과 JPA 표준 기능은 다르다
  • DB 레벨에서 처리되는 것과 애플리케이션 레벨에서 처리되는 것의 차이를 이해해야 한다
  • 영속성 컨텍스트의 일관성은 JPA를 쓸 때 항상 신경 써야 하는 부분이다

카카오테크캠퍼스 3기 선물하기 API 클론 코딩 중 멘토님 피드백 정리.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.