Back-end/Spring

Spring(5) - @Transactional

HOONY_612 2021. 8. 18. 15:46
반응형

 

 

이번 글은 흔히 쓰이는 @Transactional 어노테이션에 대해서 알아보려고합니다.

앞서 트랜잭션에 관한 글을 읽고 이 글을 읽으시면 이해가 잘될것입니다.

 

트랜잭션 어노테이션의 내부 동작은 크게 3가지로 나뉩니다.

PlatformTransactionManager을 인터페이스로 활용하고 있습니다. 안의 메소드를 살펴보겠습니다.

public interface PlatformTransactionManager {
    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
    void commit(TransactionStatus status) throws TransactionException;
    void rollback(Transaction status) throws TransactionException;
}

첫 번째 메소드는 getTransaction입니다. 이것은 매개변수로 트랜잭션 전파를 가져와 그에 맞는 트랜잭션을 얻는 메소드입니다.

두 번째는 메소드는 commit입니다. 이것은 트랜잭션이 오류가 발생하지 않고 완료되었을 때 사용되는 메소드입니다.

마지막 rollback메소드는 트랜잭션 도중 오류가 발생했을 때 이전 상태로 되돌려 주는 메소드입니다.

위의 인터페이스 구현체로는 8가지 정도가 있습니다. 그 중 주로 사용하는 구현체 3가지를 소개하겠습니다.

1. JtaTransactionManager

Jta(Java Trasaction API)는 하나가 아닌 여러개의 DB에 다중 접근하여 트랜잭션을 수행하는 경우 사용됩니다.

 

2. JpaTransactionManager

JPA를 사용하는 경우에 사용하며 EntityManagerFactory가 트랜잭션을 관리하게 됩니다.

 

 

3. DataSourceTransactionManager

현재는 잘 사용하지 않지만 Mybatis나 JDBC를 이용하는 경우 Datasource를 전달받아 트랜잭션을 관리합니다.

 

spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none


@Autowired
private DataSource dataSource;

public void Test() {
    System.out.println("dataSource = " + dataSource);
}

//data = HikariDataSource (HikariPool-1)

위의 예시들과 같이 직접 코드로 매니저를 생성하고 set을 이용하여 관리할 수 있습니다.

그러나 이것 이외에 선언적 트랜잭션(tx namespace)이나 어노테이션(@Transaction)으로도 트랜잭션을 관리 할 수 있습니다.

 

선언적 트랜잭션은 직접 매니저를 Bean으로 등록하고 속성과 대상을 정의(appropriate.properties)해 트랜잭션을 사용합니다.

장점으로는 코드에 전혀 영향을 주지 않습니다.

 

다음으로 제일 중요한 어노테이션기반(@Transactional)의 트랜잭션 관리입니다.

이 부분이 궁금해 앞의 포스팅부터 지금까지 달려왔습니다.... 이제 드디어 살펴보도록 하죠!

 

우선 @Transactional은 주로 Service계층에서 사용됩니다. 다른 곳에도 물론 사용이 가능합니다.

인터페이스, 클래스, 메소드 등등 다양한 곳에 사용가능하지만 중복으로 선언이 되는 경우가 있습니다.

 

@Transactional
public class Service {
	
    @Transactional
    void printName() {
    	System.out.println("jihuhwan");
    }
}

이러한 경우에는 우선순위가 적용됩니다.

클래스 -> 클래스 메서드 -> 인터페이스 -> 인터페이스 메서드 순서로 적용된다고 보시면 됩니다.

그럼 @Transactional이 무슨 특성을 가지고 있는지 알아보겠습니다.

 

크게 두 가지로 분류되어집니다.

첫 번째 속성은 Propagation(전파)속성입니다. 즉 트랜잭션이 겹칠 경우 어떻게 작동할 것인지 정해주는 것입니다.

두 번째 속성은 IsolationLevel(격리레벨)을 정해주는 것입니다.

여러 트랜잭션이 DB에 요청 할 경우 데이터를 어떻게 다룰 것인지 정해주는 것입니다.

 

 

@Transactional의 설정 요소

 

1. Propagation(전파)

Propagation(전파)속성부터 알아보겠습니다.

  진행 트랜잭션이 있을 경우 진행 트랜잭션이 없을 경우
REQUIRED 진행 트랜잭션 사용 새 트랜잭션 생성 후 실행
REQUIRES_NEW 진행 트랜잭션 보류 후 새 트랜잭션 실행 새 트랜잭션 생성 후 실행
SUPPORTS 진행 트랜잭션 사용 트랜잭션 없이 실행
NOT_SUPPORTED 진행 트랜잭션 보류 트랜잭션 없이 실행
MADATORY 진행 트랜잭션 사용 예외 발생
NEVER 예외 발생 트랜잭션 없이 실행
NESTED 내부 트랜잭션 생성 새 트랜잭션 생성 후 실행

위의 표를 보면서 필요할 때 적재적소에 맞게 속성을 지정해주면 됩니다.

그 다음 트랜잭션의 제일 골치 아픈 숙제가 있습니다. 바로 동시성 문제입니다.

여러 트랜잭션이 같이 실행되는 경우 DB에서는 많은 문제가 발생 할 수 있습니다. 그러한 문제들을 살펴보겠습니다.

 

 

2. 트랜잭션 시 문제

 

Dirty-Read

Dirty-Read같은 경우는 말 그대로 잘못 읽힌다는 뜻입니다. 아래의 예시를 들어보겠습니다.

아래에서는 현재 jihun이라는 이름은 조회가 되면 안되는 상황입니다. 그러나 Transaction2는 jihun을 조회 할 수 있습니다.

만약 Transaction1이 Rollback을 하면 jihun은 DB에 존재하지 않습니다. 이러한 경우를 Dirty-Read라고 합니다.

 

Non-Repeatable Read

Non-Repeatable Read는 트랜잭션이 어떤 값을 반복조회하는 경우 조회 값이 달라질 수 있는 경우를 말합니다. 예시를 보시죠.

지금 jihun은 초기에 id 값을 1을 가져있다고 가정합시다. 그리고 중간에 2로 바꿔치기 했습니다.

트랜잭션2에서는 조회를 2번 실행하는 로직이 있는데 조회 할 때 값이 다르게나오면 어떻게 될까요?

이러한 오류를 Non-Repeatable Read라고 합니다.

 

Phantom-Read

Phantom-Read같은 경우에는 위의 Non-Repeatable Read 유사하지만 다른 점이있습니다.

조회 할 때는 똑같이 나오지만 트랜잭션1에서 만약 Insert나 Delete를 실행했을 경우에는 레코드가 늘거나 줄게됩니다.

이렇게 레코드 수에 영향을 받는 문제가 Phantom-Read입니다.

 

 

3. Isolation Level

대표적인 3가지 문제에 대해서 자세하게 알아봤습니다. 이것을 해결하기 위해서 격리레벨을 설정해줘야합니다.

격리레벨은 다음과 같은 그림으로 구성됩니다. 그럼 무조건 Serializable해주면 안되냐? 라고 생각하실 수 있습니다.

그러나 Serializable은 성능이 떨어지기 때문에 사용을 지양합니다.

READ-UNCOMMITTED

이 특성은 커밋되지 않은 데이터를 다른 트랜잭션이 읽어들일 수 있게하는 설정입니다.

 

READ-COMMITTED

커밋된 상태의 값만 읽을 수 있게하는 설정입니다.

 

REPEATABLE-READ

트랜잭션의 범위내의 내용은 항상 동일하게 조회하게 하는 기능입니다.

 

SERIALIZABLE

여러 개의 트랜잭션을 사용 시 한 트랜잭션이 사용하는 데이터는 다른 트랜잭션이 사용하지 못하도록하게 하는 기능입니다.

 

 

 

이렇게 Spring에서의 트랜잭션관리에 대해서 알아봤습니다.

나중에 많은 사용자요청이 오는 서버스를 구축할 때 이 부분에 신경을 많이 써야될 것 같습니다. 

지금은 개념만 잡고 나중에 실무로 나갔을 때 잘 대처해낼 수 있었으면 좋겠습니다.

긴 글 읽어주셔서 감사합니다.

반응형