Spring에서의 트랜잭션

 

 

트랜잭션은 크게 로컬 트랜잭션분산 트랜잭션으로 구분된다.

 

Spring에서 사용되는 트랜잭션 방식은 선언형 트랜잭션 방식프로그래밍 코드 베이스 트랜잭션 방식이 있다.

 

트랜잭션은 애플리케이션 내에서 핵심 로직이 아닌 부가 기능이기 때문에 AOP 적용 대상이다.

➡️ 프로그래밍 코드 베이스 방식으로 트랜잭션을 적용하는 방식은 적절하지 않다.

 

 

 

Spring에서 선언형 방식의 트랜잭션 적용

 

Spring에서 선언형 방식으로 트랜잭션을 적용하는 방식은 크게 두가지가 있다.

1. 작성한 비즈니스 로직에 애너테이션을 추가

2. AOP 방식을 이용해서 비즈니스 로직에서 아예 트랜잭션 적용 코드를 감추는 방식

 

 

 

Spring Boot에서의 트랜잭션 설정

 

트랜잭션은 데이터베이스 인터랙션과 관련이 있기 때문에 커넥션 정보를 포함하고 있는 Datasource 가 기본적으로 필요하다.

Spring에서 트랜잭션은 기본적으로 PlatformTransactionManager 인터페이스에 의해 관리된다.

PlatformTransactionManager를 구현해서 유연하게 트랜잭션을 적용할 수 있다.

JPA 데이터 액세스 기술은 PlatformTransactionManager의 구현 클래스인 JpaTransactionManager를 사용한다.

 

 

 

 

애너테이션 방식의 트랜잭션 적용

 

@Transactional

Spring에서 가장 간단하게 트랜잭션을 적용할 수 있는 방법이다.

클래스에 추가하면 모든 메서드에 적용되서 어떤 메서드든 호출하면 하나의 새로운 트랜젝션이 생성된다.

rollback 없이(에러나 예외 없이) 메서드 호출이 끝나면 트랜젝션에서 commit이 일어나고 트랜젝션이 종료된다.

중간에 예외가 발생할 경우 rollback 되서 데이터베이스가 변경됐던 부분이 반영되지 않는다.

 

 

▶️ JPA 로그 레벨 설정

 

application.yml(resources 디렉토리에 있다.)에 해당 코드 추가

 

logging:
  level:
    org:
      springframework:
        orm:
          jpa: DEBUG

 

로그 레벨을 ‘DEBUG’ 레벨로 설정 ➡️ JPA 내부에서 ‘DEBUG’ 로그 레벨을 지정한 부분의 로그를 확인할 수 있다.

 

 

✔️ 참고) 로그 레벨

 TRACE  <  DEBUG  <  INFO  <  WARN  <  ERROR

  • ERROR : 요청을 처리하는 중 오류가 발생한 경우 표시한다.
  • WARN : 처리 가능한 문제, 향후 시스템 에러의 원인이 될 수 있는 경고성 메시지를 표시한다.
  • INFO : 상태변경과 같은 정보성 로그를 표시한다.
  • DEBUG : 프로그램을 디버깅하기 위한 정보를 표시한다.
  • TRACE : Debug보다 훨씬 상세한 정보를 나타낸다.

 

 

 

체크 예외(checked exception)는 rollback이 잘 될까?

 

체크 예외 예시 : Exception, SQLException, DataFormatException,...

체크 예외(checked exception)는 @Transactional 애너테이션만 추가해서는 rollback 되지 않는다.

체크 예외는 캐치(catch)한 후에 어떻게 처리할 지 결정해야 한다.

만일 예외 전략을 짤 필요가 없다면

@Transactional(rollbackFor = {SQLException.class, DataFormatException.class})

같이 체크 예외를 직접 지정해주거나 언체크 예외(unchecked exception)로 감싸서 rollback을 수행한다.

 

 

 

▶️ 메서드 레벨에 @Transactional 적용

 

메서드 레벨에도 @Transactional 에너테이션을 적용할 수 있다.

메서드 위에 작성하고 작성한 메서드만 트랜젝션을 적용한다.

 

Attribute

  • readOnly
    • 값으로 true 를 주면 읽기 전용 메서드가 된다.
    • commit 절차는 진행한다.
    • JPA 내부적으로 영속성 컨텍스트를 flush 하지 않는다. (변경 감지를 위한 스냅샷 생성 x)
    • 내부적으로 불필요한 추가 동작을 줄여서 성능을 최적화 할 수 있다.

 

 

※ @Transactional (readOnly = true)

➡️ readOnly를 찾을 수 없다고 뜨는 경우

Transactional import를 javax.transaction 이 아닌

org.springframework.transaction.annotation.Transactional 에서 해야 한다.

두 개의 라이브러리에 tranasactional 애너테이션이 존재하므로 import 할 때 주의한다.

 

 

▶️ 클래스 레벨과 메서드 레벨의 트랜잭션 적용 순서

 

✔️ 클래스 레벨에만 @Transactional이 적용된 경우

➡️ 클래스 레벨의 @Transactional이 메서드에 일괄 적용된다.

 

✔️ 클래스 레벨과 메서드 레벨에 함께 적용된 경우

메서드 레벨의 @Transactional이 적용된다.

만약 메서드 레벨의 @Transactional이 적용되지 않았다면 클래스 레벨의 @Transactional 이 적용된다.

 

 

 

➤ 트랜잭션 전파(Transaction Propagation)

 

@Transactional(propagation = Propagation.REQUIRED)

 

트랜잭션 Propagation?

트랜잭션의 경계에서 진행 중인 트랜잭션이 존재할 때 또는 존재하지 않을 때, 어떻게 동작할 것인지 결정하는 방식을 의미한다.

 

propagation attribute를 통해 설정할 수 있다.

 

▶️ propagation의 대표적 유형

1. Propagation.REQUIRED

propagation 중 가장 일반적으로 사용된다.

현재 진행 중인 트랜잭션이 존재하면 해당 트랜잭션을 사용하고, 존재하지 않으면 새 트랜잭션을 생성하도록 해준다.

 

2. Propagation.REQUIRES_NEW

이미 진행 중인 트랜잭션과 무관하게 새로운 트랜잭션이 시작된다.

기존에 진행중이던 트랜잭션은 새로 시작된 트랜잭션이 종료할 때까지 중지된다.

 

3. Propagation.MANDATORY

진행 중인 트랜잭션이 없으면 예외를 발생시킨다.

 

4. Propagation.NOT_SUPPORTED

트랜잭션을 필요로 하지 않다는 것을 의미한다.

진행 중인 트랜잭션이 있으면 메서드 실행이 종료될 때까지 진행중인 트랜잭션은 중지하며, 메서드 실행이 종료되면 트랜잭션을 계속 진행한다.

 

5. Propagation.NEVER

트랜잭션을 필요로 하지 않다는 것을 의미한다.

진행중인 트랜잭션이 존재할 경우에 예외를 발생시킨다.

 

 

 

➤ 트랜잭션 격리 레벨(Isolation Level)

 

트랜잭션은 다른 트랜잭션에 영향을 주지 않고, 독립적으로 실행되어야 하는 격리성이 보장되어야 한다.(ACID 원칙 중 Isolation)

트랜잭션 Isolation은 동시 트랜잭션에 의해 적용된 변경 사항이 서로에게 어떻게 보이는지 설정한다.

각각의 isolation 레벨은 트랜잭션이 동시에 발생할 때 생길 수 있는 아래 문제들을 예방한다.

  1. Dirty read : 동시 트랜잭션에서 커밋되지 않은 변화를 읽을 수 있다.
  2. Nonrepeatable read : 동시 트랜잭션이 동일한 행을 업데이트하고 커밋하는 경우 행을 다시 읽을 때 다른 값을 얻는다.
  3. Phantom read : 다른 트랜잭션이 범위의 일부 행을 추가하거나 제거하고 커밋하는 경우 쿼리를 다시 실행한 후 다른 행을 가져온다.

Spring 에서 @Transactional 의 attribute로 isolation을 통해 트랜잭션 간의 격리성을 제공한다.

 

1. Isolation.DEFAULT

데이터베이스에서 제공하는 기본 값

 

2. Isolation.READ_UNCOMMITTED

다른 트랜잭션에서 커밋하지 않은 데이터를 읽는 것을 허용한다.

 

3. Isolation.READ_COMMITTED

다른 트랜잭션에 의해 커밋된 데이터를 읽는 것을 허용한다.

 

4. Isolation.REPEATABLE_READ

트랜잭션 내에서 한 번 조회한 데이터를 반복해서 조회해도 같은 데이터가 조회되도록 한다.

 

5. Isolation.SERIALIZABLE

동일한 데이터에 대해서 동시에 두 개 이상의 트랜잭션이 수행되지 못하도록 한다.

 

 

https://www.baeldung.com/spring-transactional-propagation-isolation

 

Transaction Propagation and Isolation in Spring @Transactional | Baeldung

Learn about the isolation and propagation settings in Spring's @Transactional

www.baeldung.com

 

 

 

 

 

AOP 방식의 트랜잭션 적용

 

 

1. AOP 방식으로 트랜잭션 적용을 위한 Configuration 클래스 정의

@Configuration 애너테이션 추가

 

2. TransactionManager DI

애플리케이션에 트랜잭션 적용하기 위해서는 TransactionManager라는 객체가 필요하다.

DI(의존성 주입)를 통해 TransactionManager 객체를 받는다.

 

3. 트랜잭션 advice용 TransactionInterceptor 빈 등록

Spring에서 TransactionInterceptor를 이용해서 대상 클래스 / 인터페이스에 트랜잭션 경계를 설정하고 트랜잭션을 적용할 수 있다.

  • 트랜잭션 attribute 지정
  • 트랜잭션을 적용할 매서드에 트랜잭션 애트리뷰트 매핑
  • TransactionInterceptor 객체 생성

 

4. Advisor 빈 등록

  • 포인트 컷 지정
  • Advisor 객체 생성

 

 

 

 

✅ 추가 학습이 필요한 부분

 

✔️ 분산 트랜잭션

서로 다른 데이터 소스를 사용하는 한 개 이상의 데이터베이스를 하나의 트랜잭션으로 묶어서 처리해야 하는 것을 분산 트랜잭션이라고 한다.

분산 트랜잭션을 적용하면 서로 다른 리소스에 대한 작업을 수행하더라도 하나의 작업처럼 관리하기 때문에 원자성을 보장할 수 있다.

WAS가 제공하는 JTA(Java Transaction API) 서비스 이용

Atomikos는 Spring Boot에서 스타터로 지원하는 가장 인기있는 오픈 소스 트랜잭션 관리자이다.

 

 

 

 

 

 

감사합니다.

오개념에 대한 지적은 늘 환영입니다. 😄