[Spring] @Transactional 이란?

2019. 10. 6. 20:25SPRING

트랜잭션이란 ?

DB에서 상태변화의 한 주기를 의미한다.

쉽게 말해서 바로 이전 커밋 이후의 작업부터, 이런저런 작업 후 최종적으로 커밋을 날리기까지의 주기이다.

 

@Transactional 어노테이션은, 해당 어노테이션이 적용되는 메소드를 하나의 트랜잭션으로 묶어주는 역할을 한다.

 

예시로, "구매자가 판매자에게 금액송금" 을 대략적인 코드로 구현해 보았다.

1) 트랜잭션 자체를 적용하지 않은 경우

2) @Transactional 대신 try-catch로 구현한 경우

3) @Transactional을 사용한 경우

아래 세가지 경우의 코드와 문제점이 각각 무엇인지 정리해 보았다.

 

 

1) 트랜잭션 자체를 적용하지 않은 경우

@Autowired
private Buyer buyer

@Autowired
private Seller seller;

public void buy() {

  buyer.send();			
  seller.receive();
      
}

이러한 경우 문제가 생길 수 있는 부분이

receive() 메소드 자체에 문제가 있어서 항상 send() 까지는 매번 실행이 되고 receive() 메소드는 실행이 되지 않을 경우

구매자는 돈을 보내서 DB상에 금액이 줄어들었는데, 판매자는 입금이 되지 않아 DB상에 금액 변동이 없는 경우이다.

 

따라서 반드시 transaction을 걸어서, receive() 가 실행되지 못하고 에러가 난 경우 반드시 send() 또한 롤백되어 원상복구 시켜주어야 한다.

 

 

2) @Transactional 대신 try-catch로 구현한 경우

@Autowired
private PlatformTransactionManager transactionManager;

@Autowired
private Buyer buyer

@Autowired
private Seller seller;

public void buy() {

  DefaultTransactionDefinition def = new DefaultTransactionDefinition();
  TransactionStatus status = transactionManager.getTransaction(def);

  try {
      buyer.send();			
      seller.receive();
      
      transactionManager.commit(status);

  } catch(Exception e) {

      transactionManager.rollback(status);

  } 
}

TransactionManager로 직접 커밋과 롤백을 시켜주었다.

그러나 쿼리를 사용하는 메소드에 매번 중복되는 코드를 작성해 주어야 하는 불편함이 있다.

 

 

3) @Transactional을 사용한 경우

@Autowired
private Buyer buyer

@Autowired
private Seller seller;

@Transactional
public void buy() {

  buyer.send();			
  seller.receive();

}

사용될 메서드 위에 @Transactional 어노테이션만 붙여주면, 위의 try-catch문을 작성하지 않아도 된다.

 

 

 

만약 아래와 같이 클래스에 속하는 모든 메소드에 @Transactional 어노테이션을 적용하고 싶다면, 어노테이션을 클래스 위에 붙여준다.

클래스 위에 @Transactional이 붙어있으면, 클래스에 속한 모든 메소드에 각각 @Transactional이 붙어있다고 생각하면 된다.

@Transactional
public class AllQuery {

  public void selectOne() { }
  public int allCount() { }
  public List<User> allUser() { }  
  
  /* 아래와 동일하다
  @Transactional
  public void selectOne() { }
  
  @Transactional
  public int allCount() { }
  
  @Transactional
  public List<User> allUser() { }  
  */  

}