Spring

[Spring] 트랜잭션 전파(Propagation) 정리

tudamoa 2025. 7. 12. 19:01

✅ 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