백엔드/Spring

[Spring] 스프링 트랜잭션 전파 Propagation

kwang2134 2024. 9. 5. 15:46
728x90
반응형
728x90

스프링 트랜잭션 전파 Propagation

  • 트랜잭션 전파는 한 트랜잭션이 다른 트랜잭션에 어떻게 영향을 미치는지를 정의
  • 트랜잭션을 각각 사용하는 것이 아닌 트랜잭션이 이미 진행 중일 때 추가로 트랜잭션을 수행하는 경우

사용법

  • @Transactional 어노테이션 속성 값으로 정의 하여 사용
  • ex) @Transactional(propagation = Propagation.REQUIRED)

기본 옵션 Required 

  • 스프링 트랜잭션의 기본 설정 옵션으로 생성된 트랜잭션이 없다면 생성하고 이미 존재한다면 참여
  • 처음 시작된 트랜잭션 1(외부)이 진행 중이고 트랜잭션 2(내부)가 시작되는 경우 스프링은 트랜잭션 1,2를 묶어 하나의 트랜잭션으로 만듦
  • 하나로 묶인 트랜잭션은 물리 트랜잭션이 되고 내부의 트랜잭션 1,2는 논리 트랜잭션이 됨
    • 물리 트랜잭션: 실제 데이터베이스에 적용되는 트랜잭션
    • 논리 트랜잭션: 애플리케이션 또는 비즈니스 로직의 관점에서 정의된 트랜잭션

커밋 요청 흐름

  1. 트랜잭션 매니저는 getTransaction()을 호출해 트랜잭션 1(외부)을 시작 
  2. 트랜잭션 매니저는 데이터소스를 통해 커넥션 생성
  3. 생성한 커넥션을 수동 커밋으로 설정 - 물리 트랜잭션 시작 단계
  4. 트랜잭션 매니저는 트랜잭션 동기화 매니저에 커넥션을 보관
  5. 트랜잭션 매니저는 트랜잭션을 생성한 결과를 TransactionStatus에 담아서 반환(신규 트랜잭션)
  6. 로직 1에 사용되고 커넥션이 필요한 경우 트랜잭션 동기화 매니저를 통해 트랜잭션이 적용된 커넥션 획득
  7. 트랜잭션 매니저는 getTransaction()을 호출해 트랜잭션 2(내부)를 시작
  8. 트랜잭션 매니저는 트랜잭션 동기화 매니저를 통해 기존 트랜잭션이 존재하는지 확인
  9. 기존 트랜잭션이 존재하므로 기존 트랜잭션에 참여
  10. 트랜잭션 매니저는 트랜잭션을 생성한 결과를 TransactionStatus에 담아 반환(신규 트랜잭션이 아님)
  11. 로직 2에 사용되고 커넥션이 필요한 경우 트랜잭션 동기화 매니저를 통해 외부 트랜잭션이 보관한 커넥션을 획득
  12. 로직 2가 끝나고 트랜잭션 매니저를 통해 내부 트랜잭션을 커밋
  13. 트랜잭션 매니저는 트랜잭션의 신규 여부에 따라 다르게 동작
    • 신규 트랜잭션: 실제 데이터베이스 커밋 호출
    • 신규 트랜잭션 x(생성된 트랜잭션에 참여 중인 상태): 커밋이나 롤백을 호출하지 않음
  14. 로직 1이 끝나고 트랜잭션 매니저를 통해 외부 트랜잭션 커밋
  15. 외부 트랜잭션(트랜잭션 1)은 신규 트랜잭션으로 실제 커밋을 호출

원칙

  • 모든 논리 트랜잭션이 커밋되어야 물리 트랜잭션이 커밋
  • 하나의 논리 트랜잭션이라도 롤백이 되면 물리 트랜잭션은 롤백

롤백 요청 흐름

  • 외부 트랜잭션 롤백
    1. 커밋의 1~11번 과정 동일
    2. 로직 2가 끝나고 트랜잭션 매니저를 통해 내부 트랜잭션을 커밋
    3. 내부 트랜잭션은 신규 트랜잭션이 아니므로 실제 커밋 x
    4. 로직 1이 끝나고 트랜잭션 매니저를 통해 외부 트랜잭션 롤백
    5. 외부 트랜잭션의 경우 신규 트랜잭션으로 데이터베이스에 실제 롤백 호출
    6. 실제 물리 롤백이 호출되고 트랜잭션 1,2 모두 롤백된 상태로 트랜잭션이 끝남
  • 내부 트랜잭션 롤백
    1. 커밋의 1~11번 과정 동일
    2. 로직 2가 끝나고 트랜잭션 매니저를 통해 내부 트랜잭션을 롤백(로직 2에 문제 발생으로 인한 롤백이 되는 경우)
    3. 내부 트랜잭션은 신규 트랜잭션이 아니므로 실제 롤백을 호출하는 대신 트랜잭션 동기화 매니저에 rollbackOnly 속성을 true로 변경 -> 해당 트랜잭션은 롤백만 가능
    4. 로직 1이 끝나고 트랜잭션 매니저를 통해 외부 트랜잭션을 커밋
    5. 외부 트랜잭션은 신규 트랜잭션으로 실제 커밋을 호출
    6. 그러나 내부 트랜잭션에서 변경한 rollbackOnly의 속성이 true로 롤백이 호출되어야 함
    7. 스프링에서 UnexpectedRollbackException 런타임 예외 발생

REQUIRES_NEW

  • 항상 새로운 트랜잭션을 시작
  • 기존 트랜잭션이 존재한다면 잠시 멈춰두고 새로운 트랜잭션을 시작
  • 외부 트랜잭션과 내부 트랜잭션을 분리해서 각각의 별도의 물리 트랜잭션을 사용하는 방법
  • 각각의 물리 트랜잭션으로 동작하기 때문에 영향을 주지 않음
  • 물리 트랜잭션이 명확하게 분리되는 만큼 데이터베이스 커넥션이 동시에 추가로 사용됨

주의 사항

  • 논리 트랜잭션에서 예외 발생 시 처리하여 정상 흐름으로 반환한 경우의 처리
  • 예외를 처리하여 로직의 흐름을 정상으로 처리하였지만 예외가 발생한 순간 트랜잭션 매니저에는 rollbackOnly가 true로 체크되고 그다음 로직이 흐름이 처리됨
  • rollbackOnly가 ture로 설정된 이상 로직이 정상 흐름으로 처리된 것과 관계 없이 무조건 물리 롤백 발생 및 UnexpectedRollbackException 발생

추가 옵션

  • SUPPORT: 트랜잭션을 지원
    • 기존 트랜잭션이 있음: 기존 트랜잭션 참여
    • 기존 트랜잭션이 없음: 트랜잭션 없이 진행
  • NOT_SUPPORT: 트랜잭션을 지원하지 않음
    • 기존 트랜잭션이 있음: 트랜잭션 없이 진행
    • 기존 트랜잭션이 없음: 트랜잭션 없이 진행(기존 트랜잭션은 보류)
  • MANDATORY: 트랜잭션이 반드시 존재해야 함
    • 기존 트랜잭션이 있음: 기존 트랜잭션 참여
    • 기존 트랜잭션이 없음: IllegalTransactionStateException 예외 발생
  • NEVER: 트랜잭션을 사용하지 않음
    • 기존 트랜잭션이 있음: IllegalTransactionStateException 예외 발생
    • 기존 트랜잭션이 없음: 트랜잭션 없이 진행
  • NESTED: 중첩 트랜잭션 생성
    • 기존 트랜잭션 있음: 중첩 트랜잭션을 만듬
      • 중첩 트랜잭션은 외부 트랜잭션의 영향을 받지만 외부에 영향을 주지는 않음
      • 중첩 트랜잭션은 롤백되어도 외부 트랜잭션 커밋 가능
      • 외부 트랜잭션이 롤백되면 중첩 트랜잭션도 함께 롤백

스프링 DB 2편 - 데이터 접근 활용 기술 강의 | 김영한 - 인프런 (inflearn.com) 강의 내용 참고

 

728x90

'백엔드 > Spring' 카테고리의 다른 글

[Spring] JPA 개요  (1) 2024.09.07
[Spring] 변경 감지 & 병합  (0) 2024.09.06
[Spring] 스프링 트랜잭션  (0) 2024.09.04
[Spring] MyBatis  (1) 2024.09.01
[Spring] JDBC Template  (3) 2024.08.31