본문으로 건너뛰기
Advertisement

11.3 트랜잭션 전파(Propagation)와 격리 수준(Isolation) 심화

단일 트랜잭션을 넘어서, 트랜잭션이 걸린 다른 메서드를 연이어 호출하거나 동시에 수천 명의 유저가 같은 DB 데이터를 수정할 때는 전파 옵션격리 수준을 세심하게 조율해야 합니다.

1. 전파 동작 (Propagation)

트랜잭션 중인 메서드가 또 다른 @Transactional 메서드를 호출할 때, "기존 트랜잭션에 합류할지, 아예 새로운 독립 트랜잭션을 파생시킬지" 결정하는 옵션입니다.

  • REQUIRED (기본값): 이미 진행 중인 부모 트랜잭션이 있다면 그곳에 조인(Join)하고, 없다면 새로 생성합니다. 부모와 자식 중 하나라도 롤백되면 양쪽 모두 롤백됩니다.
  • REQUIRES_NEW: 부모 트랜잭션의 존재 유무와 관계없이, 항상 자신만의 별도 트랜잭션을 새로(New) 엽니다. 자식이 예외를 던져 롤백되어도 부모 로직이 그 예외를 try-catch로 잡는다면, 부모 트랜잭션은 정상적으로 커밋(Commit)될 수 있습니다. (예: 주요 결제 로그 기록은 실패해도 결제 자체는 성공시키고 싶을 때)
  • SUPPORTS: 부모 트랜잭션이 있으면 합류하고, 없으면 트랜잭션 없이(Non-transactional) 동작합니다.
  • MANDATORY: 반드시 부모 트랜잭션이 있어야만 동작하며, 없으면 런타임 예외를 즉각 던집니다.
@Service
public class OrderService {
@Transactional(propagation = Propagation.REQUIRED)
public void createOrder() {
// ...
logService.saveLog(); // REQUIRES_NEW로 별도 트랜잭션 처리됨
}
}

2. 격리 수준 (Isolation Level)

동시에 여러 트랜잭션이 같은 데이터를 향해 돌진할 때, 어디까지 상태를 가려줄(Lock) 것인가를 정의합니다. 격리 수준이 높을수록 정합성(데이터 정확도)은 올라가지만, 동시성 병목(DB 락 대기)이 심각해져 서버 성능이 저하됩니다.

  1. READ_UNCOMMITTED: 커밋되지 않은 타인의 변경 데이터까지 읽어버립니다. (Dirty Read 발생, 실무 사용 금지)
  2. READ_COMMITTED (오라클, PostgreSQL 기본값): 오직 커밋된 데이터만 읽습니다. 단점은, 한 트랜잭션 안에서 2번 조회하는 사이에 타인이 데이터를 고치면 1차와 2차 조회 결과가 달라집니다 (Non-Repeatable Read).
  3. REPEATABLE_READ (MySQL 기본값): 내 트랜잭션이 열렸을 당시의 스냅샷 버전만 바라보도록 고정하여, 도중에 남이 데이터를 고쳐도 나는 항상 동일한 조회 결과를 보장받습니다.
  4. SERIALIZABLE: 가장 엄격한 격리. 모든 트랜잭션이 마치 한 줄 서기(순차적 단일 스레드)처럼 실행됩니다. 성능이 최악이라 극단적인 금융 동시성 외에는 쓰지 않습니다.
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void updateInventory() {
// 상품 재고 차감 로직
}
Advertisement