백엔드/Spring

[Spring] 스프링 트랜잭션

kwang2134 2024. 9. 4. 15:20
728x90
반응형
728x90

스프링 트랜잭션 Spring Transaction

  • 스프링 프레임워크에서 데이터베이스의 트랜잭션 관리를 지원하는 핵심 기능
  • 스프링은 PlatformTransactionManager 인터페이스를 통해 트랜잭션을 추상화

PlatformTranscationManager

  • 스프링 트랜잭션 추상화의 핵심 인터페이스
  • 주요 구현체
    • DataSourceTransactionManager(JDBC)
    • JpaTransactionManager(JPA)
    • HibernateTransactionManger(Hibernate)

사용법

  • 선언적 트랜잭션 관리
    • 스프링은 주로 XML 설정 또는 어노테이션을 통해 트랜잭션을 선언적으로 관리
    • @Transactional 어노테이션을 선언하여 사용
    • 이름 그대로 해당 로직에 트랜잭션을 적용하겠다고 어딘가에 선언하기만 하면 트랜잭션이 적용되는 방식
  • 프로그래밍 방식 트랜잭션 관리
    • 프로그래밍적으로 트랜잭션을 제어할 수 있는 API도 제공하며, 필요에 따라 트랜잭션의 시작, 커밋, 롤백 등을 직접 처리할 수 있음
    • 직접 트랜잭션에 대한 코드를 작성하여 관리하는 방법
    • TransactionTemplate 클래스를 이용한 방법도 가능

선언적 트랜잭션과 AOP

  • 선언적 트랜잭션 방식을 사용할 경우 기본적으로 프록시 방식의 AOP가 적용
  • 스프링 트랜잭션 관리는 AOP 기반으로 동작

프록시 유형

  • JDK 동적 프록시: 인터페이스 기반
  • CGLIB 프록시: 클래스 기반, 서브클래싱을 통한 프록시 생성

프록시 도입 전

  1. 트랜잭션 시작: 비즈니스 로직의 메서드 호출 시 트랜잭션을 직접 시작
  2. 비즈니스 로직 수행: 트랜잭션 내에서 비즈니스 로직을 수행, 트랜잭션 상태 유지
  3. 트랜잭션 커밋/롤백: 비즈니스 로직이 성공적이면 커밋, 예외가 발생하면 롤백 -> 직접 커밋과 롤백을 호출
  4. 트랜잭션 종료: 트랜잭션의 커밋 또는 롤백 후, 트랜잭션 리소스를 정리

프록시 도입 후

  1. 프록시 생성:
    • @Transactional 어노테이션에 따라 트랜잭션 관리 Aspect를 적용하기 위해 프록시를 생성
    • 프록시는 실제 비즈니스 로직을 감싸는 역할
  2. 트랜잭션 처리:
    • 비즈니스 로직 메서드가 호출되면 프록시가 트랜잭션을 시작하고 메서드를 호출
    • 메서드 실행 전후에 트랜잭션의 커밋 또는 롤백을 처리
  3. 비즈니스 로직 수행:
    • 프록시 내부에서 실제 비즈니스 로직 메서드가 호출
    • 메서드는 트랜잭션 관리의 영향을 받으며, 비즈니스 로직은 트랜잭션이 설정된 상태에서 실행
  4. 트랜잭션 종료:
    • 프록시는 비즈니스 로직 메서드의 실행이 완료되면 트랜잭션을 커밋하거나 롤백
    • 트랜잭션 관리 로직은 프록시 내부에서 처리되므로 개발자가 직접 트랜잭션을 관리할 필요가 없음

트랜잭션 적용 위치

  • 스프링에서의 우선순위는 항상 더 구체적이고 자세한 것이 높은 우선순위를 가짐
  • 메서드와 클래스에 어노테이션을 붙일 수 있다면 구체적인 메서드가 우선으로 적용

트랜잭션 AOP 주의 사항

  • @Transactional은 스프링 트랜잭션 AOP가 적용
  • 트랜잭션 AOP 방식은 기본적으로 프록시를 사용
  • 트랜잭션이 적용되기 위해선 무조건 프록시를 통해 호출이 되어야 함

1. 객체 내부에서 메서드 호출이 일어나는 경우

  1. @Transcational이 붙어 있지 않은 external() 메서드 호출
  2. 트랜잭션이 적용되지 않기 때문에 프록시를 통해서 호출되는 것이 아니라 실제 객체 인스턴스를 호출
  3. 호출된 메서드 내에서 같은 객체 내부 메서드인 internal() 메서드를 호출(internal() 메서드는 @Transactional)
  4. @Transactional로 트랜잭션이 적용되기 위해서는 프록시를 통해 메서드가 호출되어야 하지만 internal()은 현재 접근 중인 객체의 내부 메서드이므로 this.internal()을 통해 메서드가 직접 호출됨
  5. 프록시를 통해 호출되지 않았으므로 @Transactional 어노테이션이 붙어있지만 트랜잭션이 적용되지 않음

해결방안

  1. 기본 this 포인터를 통해 호출되지 않게 해당 메서드를 별도의 클래스로 분리하여 사용
  2. 자기 자신을 객체로 가지는 self 참조를 통해 메서드를 호출

2. 초기화 시점 사용

  • @PostConstruct를 통한 초기화 시점에서 @Transactional을 사용할 경우 적용되지 않음
  • 초기화 코드가 먼저 호출되고 그다음 트랜잭션 AOP가 적용

해결방안

  • ApplicationReadyEvent 사용
  • ApplicationReadyEvent는 트랜잭션 AOP를 포함한 스프링이 컨테이너가 완전히 생성되고 난 다음에 이벤트가 붙은 메서드를 호출하여 트랜잭션 적용가능

@Transactional 옵션

  • rollbackFor: 특정 예외가 발생할 때 트랜잭션을 롤백하도록 지정
  • noRollbackFor: 특정 예외가 발생해도 트랜잭션을 롤백하지 않도록 지정
  • isolation: 트랜잭션의 격리 수준
    • DEFAULT: 데이터베이스의 기본 격리 수준을 사용
    • READ_UNCOMMITTED: 가장 낮은 격리 수준으로, 커밋되지 않은 데이터도 읽을 수 있음
    • READ_COMMITTED: 커밋된 데이터만 읽을 수 있음
    • REPEATABLE_READ: 트랜잭션이 시작된 이후에는 읽은 데이터가 일관되게 유지
    • SERIALIZABLE: 가장 높은 격리 수준
  • timeout: 트랜잭션 타임아웃 설정
  • readOnly: 트랜잭션이 읽기 전용인지 지정(Default = false)
  • propagation: 트랜잭션의 전파 행동을 정의

테스트 주의 사항

  • @Transactional을 테스트 메서드에 사용할 경우 테스트 완료 후 자동 롤백
  • 실제 커밋 동작 테스트 시 @Commit 어노테이션 사용 필요

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

 

728x90

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

[Spring] 변경 감지 & 병합  (0) 2024.09.06
[Spring] 스프링 트랜잭션 전파 Propagation  (0) 2024.09.05
[Spring] MyBatis  (1) 2024.09.01
[Spring] JDBC Template  (3) 2024.08.31
[Spring] 예외 처리  (1) 2024.08.30