Skip to content

Spring 트랜잭션

트랜잭션 전파(Propagation)

스프링에서 하나의 트랜잭션이 진행 중일 때, 다른 메서드가 트랜잭션을 어떻게 이어받거나 새로 시작할지를 결정하는 속성.
즉, 이미 트랜잭션이 존재하는 상황에서 새로운 메서드가 호출되면 어떻게 할까?를 정의하는 것

스프링은 @Transactional(propagation = Propagation.XXX) 형태로 전파 속성을 지정할 수 있다.
전파 속성은 트랜잭션 경계를 중첩하거나 분리할 때의 동작을 제어한다.

전파 속성 종류

속성설명요약
REQUIRED (default)이미 트랜잭션이 있으면 참여, 없으면 새로 시작가장 일반적
REQUIRES_NEW항상 새로운 트랜잭션 시작 (기존 트랜잭션 일시 정지)독립 트랜잭션
SUPPORTS트랜잭션이 있으면 참여, 없으면 비트랜잭션 실행선택적 참여
MANDATORY반드시 기존 트랜잭션이 있어야 함, 없으면 예외 발생강제 참여
NOT_SUPPORTED트랜잭션을 사용하지 않음 (있어도 일시 정지)비트랜잭션
NEVER트랜잭션이 있으면 예외 발생트랜잭션 금지
NESTED부모 트랜잭션 내에 중첩 트랜잭션 생성 (Rollback 분리 가능)savepoint 기반 중첩

주요 속성 예시

REQUIRED (기본)

kotlin
@Transactional
fun serviceA() {
    serviceB() // serviceB도 REQUIRED라면 같은 트랜잭션 안에서 실행
}
  • 같은 트랜잭션으로 묶임 → 하나라도 실패 시 전체 롤백

REQUIRES_NEW

kotlin
@Transactional
fun mainTx() {
    serviceA() // 기존 트랜잭션
    serviceB() // REQUIRES_NEW -> 별도 트랜잭션
}
  • 독립적인 트랜잭션으로 실행됨
  • serviceB() 실패해도 mainTx()는 커밋 가능
  • 보상 트랜잭션, 로그 저장(로그는 실패해도 본 트랜잭션은 유지) 등에 자주 사용

NESTED

kotlin
@Transactional
fun parent() {
    nestedService() // @Transactional(propagation = Propagation.NESTED)
}
  • 부모 트랜잭션 내에서 savepoint 기반 부분 트랜잭션 생성
  • 중첩 트랜잭션만 롤백 가능 (부모는 유지)

트랜잭션 격리 수준 (Isolation Level)

격리 수준은 동시에 여러 트랜잭션이 수행될 때, 서로의 데이터 접근을 얼마나 허용할지를 결정한다.
즉, 트랜잭션 간 데이터 일관성의 보장 정도를 나타낸다.

스프링은 @Transactional(isolation = Isolation.XXX) 형태로 격리 수준을 지정할 수 있다.

격리 수준 종류

격리 수준설명허용되는 이상 현상
READ_UNCOMMITTED커밋되지 않은 데이터 읽기 허용Dirty Read, Non-repeatable Read, Phantom Read
READ_COMMITTED커밋된 데이터만 읽기Non-repeatable Read, Phantom Read
REPEATABLE_READ같은 트랜잭션 내에서 같은 쿼리 결과 보장Phantom Read
SERIALIZABLE완전한 직렬 실행 (가장 엄격, 느림)없음

이상 현상(Anomaly) 종류

이름설명예시
Dirty Read다른 트랜잭션이 아직 커밋하지 않은 데이터를 읽음A가 수정 중인데 B가 읽음
Non-repeatable Read같은 쿼리를 두 번 실행했는데 결과가 다름중간에 다른 Tx가 수정
Phantom Read조건에 맞는 행의 개수가 바뀜중간에 행이 추가됨

DBMS 기본 격리 수준

DBMS기본 수준
MySQL (InnoDB)REPEATABLE_READ
PostgreSQLREAD_COMMITTED
OracleREAD_COMMITTED
SQL ServerREAD_COMMITTED

성능과 일관성의 Trade-off

  • 격리 수준이 높을수록 데이터 일관성은 높지만 성능은 저하
  • 일반적인 API 서버는 READ_COMMITTED 사용
  • 금융/정산 시스템 등 정합성이 중요한 경우 REPEATABLE_READ 또는 SERIALIZABLE 고려