✅ 1. 트랜잭션이란?
- 트랜잭션 = DB 작업을 하나의 단위로 묶어서 처리
- 모두 성공 → 커밋(commit)
- 중간에 문제 → 롤백(rollback)
예) 은행 이체
- A 계좌 → B 계좌로 5만 원 이체
- 작업 1: A 계좌 -5만 원
- 작업 2: B 계좌 +5만 원
- 작업 1, 2를 둘 다 성공해야 트랜잭션 commit, 실패하면 rollback
✅ 2. @Transactional
- 스프링에서 메서드에 @Transactional 붙이면
- 메서드 시작 → 트랜잭션 시작
- 예외 발생 → 롤백
- 정상 끝 → 커밋
- 여러 트랜잭션을 묶어서 하나의 트랜잭션 경계를 만들 수 있다.
만약 게시글을 작성(하나의 트랜잭션) 하는 애플리케이션에서 임시저장 기능과 로그저장 기능을 동시에 수행해야 할 때,
임시저장은 성공하였지만 로그저장 기능이 실패 했을 때 임시저장 기능까지 rollback 시켜야 할까?
로그만 rollback을 시키고 임시 저장은 commit 시키는 방법은 안될까?
✅ 3. 전파(propagation)란?
이미 트랜잭션이 진행 중일 때, 또 다른 트랜잭션이 필요하다면 어떻게 할지를 정하는 설정
- 부모 메서드에서 트랜잭션이 돌고 있는 상태에서
- 그 안에서 또 다른 메서드를 호출 → 그 메서드도 @Transactional이라면?
이때 어떻게 할지 결정하는 게 전파(propagation)
같이 쓸 것인가, 새로 시작할 것인가, 아예 트랜잭션이 없이 할 것인가
✅ 4. 물리 트랜잭션 vs 논리 트랜잭션
✔ 물리 트랜잭션 (Physical Transaction)
- DB connection에 실제로 시작/커밋/롤백을 명령하는 트랜잭션
- 진짜로 DB 가 인지하는 트랜잭션
✔ 논리 트랜잭션 (Logical Transaction)
- 스프링 코드 관점에서 트랜잭션처럼 보이는 단위
- 여러 @Transactional 메서드들이 같은 물리 트랜잭션을 공유할 수 있음
모든 논리 트랜잭션이 커밋되어야 물리 트랜잭션이 커밋된다.
하나의 논리 트랜잭션이라도 롤백되면 물리 트랜잭션은 롤백된다.
✔ 예시
@Transactional
public void outerMethod() {
innerMethod();
}
@Transactional
public void innerMethod() {
// 같은 로직
}
- 물리 트랜잭션: 하나
- 논리 트랜잭션: outerMethod(), innerMethod() 두 개처럼 보이지만 실제로 같은 트랜잭션
✅ 5. Propagation 종류 & 특징
✔ 1) REQUIRED (기본값)
- 이미 트랜잭션 있으면 그대로 사용
- 없으면 새로 시작
→ 가장 흔히 사용됨, 단순히 메서드 여러 개를 하나의 트랜잭션으로 묶고 싶으면 사용
@Transactional
public void outer() {
inner();
}
@Transactional(propagation = Propagation.REQUIRED)
public void inner() {
// outer()와 같은 트랜잭션
}
- 하나라도 오류 나면 둘 다 롤백됨
- 물리 트랜잭션 수: 1
- 논리 트랜잭션 수: 2 이상
- 물리, 논리 트랜잭션이 하나로 묶임
✔ 2) REQUIRES_NEW
- 무조건 새 트랜잭션 시작
- 기존 트랜잭션은 잠깐 멈춤 (suspend)
@Transactional
public void outer() {
inner();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void inner() {
// 새로운 트랜잭션
}
- inner()에서 예외 발생 → outer() 트랜잭션에는 영향 없음
- 트랜잭션 분리하고 싶을 때 유용
- 물리 트랜잭션 수: 2 이상
- 논리 트랜잭션 수: 2 이상
- 물리, 논리 트랜잭션이 새로 생성
✔ 3) NESTED
- 중첩(자식) 트랜잭션을 생성하며 부모 트랜잭션 안에 savepoint 생성
- 자식만 롤백 가능 → 자식에 상관없이 부모는 계속 진행 가능
- 이미 트랜잭션 있으면 중첩 트랜잭션 생성
- 없으면 새로 시작
단, JDBC 드라이버가 savepoint 지원해야 사용 가능
@Transactional
public void outer() {
inner();
}
@Transactional(propagation = Propagation.NESTED)
public void inner() {
// savepoint created
}
- inner()에서 rollback → savepoint 까지만 롤백
- outer()는 영향 안 받고 진행 가능
- 부모 롤백 없이 부분 롤백 처리하고 싶을 때 사용
- 물리 트랜잭션 수: 1
- 논리 트랜잭션 수: 2
✔ 4) SUPPORTS
- 트랜잭션 있으면 참여
- 없으면 트랜잭션 없이 실행
@Transactional
public void outer() {
inner();
}
@Transactional(propagation = Propagation.SUPPORTS)
public void inner() {
// 존재하면 트랜잭션 참여
}
- outer() 없으면 inner() 트랜잭션 없이 수행됨
✔ 5) NOT_SUPPORTS
- 트랜잭션 있으면 일시 중지 (suspend)
- 트랜잭션 없이 실행
@Transactional
public void outer() {
inner();
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void inner() {
// 비트랜잭션
}
- outer()가 트랜잭션이어도 inner()는 비트랜잭션 상태
✔ 6) NEVER
- 트랜잭션 있으면 예외 발생
- 트랜잭션 없이만 실행해야 함
@Transactional
public void outer() {
inner();
}
@Transactional(propagation = Propagation.NEVER)
public void inner() {
// 트랜잭션이 존재하면 예외 발생
}
- outer() 트랜잭션 존재 → inner() 호출 시 예외 터짐
✔ 7) MANDATORY
- 반드시 기존 트랜잭션 존재해야 함
- 없으면 예외 발생
public void outer() {
inner();
}
@Transactional(propagation = Propagation.MANDATORY)
public void inner() {
// 트랜잭션이 없으면 예외 발생
}
- outer() 트랜잭션 없으면 예외 발생
'Spring' 카테고리의 다른 글
| [Spring] AOP & Proxy 란? (3) | 2025.08.01 |
|---|---|
| [Spring] JDBC Template 정리 (0) | 2025.05.31 |
| [Spring] MVC 정리 (0) | 2025.04.12 |
| [Spring] IoC / DI 정리 (0) | 2025.03.22 |
| [Spring] 빈 / 빈 스코프(Bean / Bean Scope) 정리 (0) | 2025.03.19 |