본문 바로가기

Spring

[Spring] Transactional

트랜잭션을 사용하는 이유 : 데이터의 일관성과 무결성을 보장하기 위해서

  • 원자성(Atomicity) → 모두 성공하거나 모두 실패해야 한다.
  • 일관성(Consistency) → 트랜잭션 전후 데이터가 항상 규칙을 지킨다.
  • 격리성(Isolation) → 동시에 실행돼도 서로 간섭하지 않는다.
  • 지속성(Durability) → 커밋된 결과는 영구적으로 보존된다.

 

@Transactional 주요 옵션 총정리

1) propagation (전파 속성)

트랜잭션이 이미 존재할 때, 현재 메서드를 어떻게 실행할지 결정

옵션 설명
REQUIRED (기본값) 현재 트랜잭션이 있으면 참여, 없으면 새로 생성
REQUIRES_NEW 항상 새 트랜잭션 생성, 기존 트랜잭션은 잠시 보류
NESTED 중첩 트랜잭션 생성, 부모 트랜잭션 안에 별도 저장점(Savepoint) 생성
SUPPORTS 트랜잭션 있으면 참여, 없으면 트랜잭션 없이 실행
NOT_SUPPORTED 트랜잭션 없이 실행, 기존 트랜잭션 있으면 잠시 중단
MANDATORY 반드시 트랜잭션 내에서 실행, 없으면 예외 발생
NEVER 트랜잭션 있으면 예외 발생, 트랜잭션 없이만 실행 가능

 

 

2) isolation (격리 수준)

동시성 문제 제어 — 잘못된 읽기 방지

옵션 설명
DEFAULT DB 기본 설정 따름
READ_UNCOMMITTED 다른 트랜잭션의 미완료 데이터도 읽기 가능 (가장 약함)
READ_COMMITTED 커밋된 데이터만 읽기 가능 (기본값인 경우 많음)
REPEATABLE_READ 트랜잭션 내 같은 데이터 반복 조회 시 값 고정
SERIALIZABLE 가장 엄격, 완전한 순차 처리 보장, 성능 저하 가능

 

cf. 트랜잭션 격리 수준 별 이상 현상 발생 여부

1) 트랜잭션 이상 현상(Anomaly) 용어
용어 문제 상황
더티 리드 커밋되지 않은 타인의 수정 데이터를 읽음 (롤백 가능성 있음) 값 변경 중에 읽었는데 롤백
노 리피트블 리드 한 트랜잭션 내에서 같은 데이터를 두 번 읽는데 값이 다름 값을 읽는 사이에 값 변경
팬텀 리드 한 트랜잭션 내에서 같은 조건 조회인데 행(ROW) 개수가 바뀜 값을 읽는 사이에 열 추가 or 삭제

2) 트랜잭션 격리 수준 요약

격리 수준 더티 리드 노 리피트블 리드 리드 팬텀 리드 실무 사용도 특징
READ_UNCOMMITTED 발생함 발생함 발생함 거의 안 씀 가장 약함, 신뢰도 낮음
READ_COMMITTED 차단 발생함 발생함 기본값인 경우 많음 커밋된 데이터만 읽기
REPEATABLE_READ 차단 차단 발생함 중요 트랜잭션에 사용 같은 데이터 재조회 값 고정
SERIALIZABLE 완전 차단 완전 차단 완전 차단 성능 이슈로 제한적 가장 엄격, 완벽한 고립 보장

 

3) rollbackFor / noRollbackFor

특정 예외 발생 시 롤백 여부 설정

@Transactional(rollbackFor = Exception.class)
  • Exception 발생 시 롤백하도록 설정

cf.

❓ 예외가 발생하면 롤백하는게 기본 설정 아닌가?

@Transactional의 기본 롤백 기준

RuntimeException 또는 그 하위 예외 발생 시 롤백됨



자바 예외 (Throwable) 계층 구조
Throwable
├── Error  → 시스템 치명적 오류 (OutOfMemoryError 등)
└── Exception
    ├── RuntimeException  → 실행 시 발생, 예외처리 선택 (Unchecked Exception)
    └── (그 외)           → 컴파일 강제 예외처리 필요 (Checked Exception)​


❓ 이때 든 의문점

@Transaction의 기본 롤백 기준이 RuntimeException이다.
RuntimeException(Unchecked Exception)이 아닌 예외는 Checked Exception이며,
Checked Exception은 컴파일 시점에서 걸리게 된다고 생각했다.
그렇다면 정상 실행된 서버에서 Checked Exception이 발생되는 경우가 있나?

✔️ 결론
정상적으로 실행되는 서버에서 checked exception이 발생할 수 있는 상황은 충분히 있다.
주로 외부 환경 요인 에서 비롯된다:
  • 파일 입출력 중 (IOException)
  • 네트워크 요청 중 (TimeoutException) (Checked 타입일 경우)
  • DB 연결 중 (SQLException)
이러한 예외들은 대부분 Checked Exception 이기 때문에 기본 설정인 @Transactional로는 자동 롤백되지 않는다.

 

❓ 예외가 발생했는데 롤백을 하지 않는 상황도 있을까?
대표적으로 사용자 입력 오류 같은 경우에는 예외가 발생해도 롤백하지 않고 데이터를 유지하는 게 더 나은 선택일 수 있다.

사용자 입력 오류 등에서 롤백하지 않는 이유
  • 트랜잭션 내 일부 작업이 사용자 실수로 실패했더라도,
    그 전까지 정상 처리된 데이터는 저장되어야 할 가치가 있음
  • 전체 트랜잭션을 롤백하면
    정상적인 데이터까지 사라지고, 사용자 경험이 나빠질 수 있음
  • 예외 상황(오류)은 별도로 처리하고,
    성공한 작업은 그대로 커밋하는 것이 더 효과적임

e.g. 사용자가 주문을 진행 중: 물건을 담고 → 할인 쿠폰 입력 시 오류 발생
  • 이 경우 할인 쿠폰 적용만 실패했을 뿐 주문 자체는 유효하므로 롤백할 이유가 없음
  • 트랜잭션 전체를 롤백하면 물건 담은 정보도 사라짐
    → 사용자 입장에서 처음부터 다시 주문해야 하는 불편함 발생

 

4) timeout

트랜잭션 최대 수행 시간 (초 단위)

@Transactional(timeout = 5)
  • 5초 넘으면 트랜잭션 강제 종료

 

5) readOnly

조회 전용 트랜잭션 (쓰기 금지)

@Transactional(readOnly = true)
  • 성능 최적화에 도움, 쓰기 시도하면 예외 발생할 수도 있음