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