[Spring] @Transactional

CodeByHan·2024년 11월 28일

스프링

목록 보기
8/33
post-thumbnail

트랜잭션(Transaction)

  • 데이터베이스의 상태를 변화시키기 위해서 수행하는 작업의 단위
  • 데이터의 정합성을 보장하기 위해 고안된 방법이다.
  • 데이터베이스 트랜잭션은 데이터베이스 관리 시스템 또는 유사한 시스템에서 상호작용의 단위
  • 여기서 유사한 시스템이란 트랜잭션이 성공과 실패가 분명하고 상호 독립적이며, 일관되고 믿을 수 있는 시스템을 의미한다.

@Transactional

  • Spring 프레임워크에서 데이터베이스 트랜잭션을 관리하기 위해 사용되는 어노테이션
  • 이 어노테이션은 트랜잭션을 선언적으로 관리할 수 있게 하며, 데이터의 일관성과 무결성을 유지하는 데 중요한 역할을 한다.

예를 들어보자

  • 은행 송금 예시
    송금 요청: 사용자가 A 계좌에서 B 계좌로 5,000원을 송금하려고 한다.
    입금 작업: B 계좌에 5,000원을 입금한다.
    예를 들어 출금은 성공했지만 입금 과정에서 오류가 발생할 경우, A 계좌에서는 5,000원이 빠져나갔지만 B 계좌에는 입금되지 않는 문제가 발생할 수 있다.
@Service
public class BankService {

    @Autowired
    private AccountRepository accountRepository;

    @Transactional
    public void transferMoney(Long fromAccountId, Long toAccountId, int amount) {
        // 1. A 계좌에서 출금
        Account fromAccount = accountRepository.findById(fromAccountId)
                .orElseThrow(() -> new AccountNotFoundException("A 계좌를 찾을 수 없습니다."));
        fromAccount.withdraw(amount);  // 출금 메소드 호출

        // 2. B 계좌에 입금
        Account toAccount = accountRepository.findById(toAccountId)
                .orElseThrow(() -> new AccountNotFoundException("B 계좌를 찾을 수 없습니다."));
        toAccount.deposit(amount);  // 입금 메소드 호출

        // 3. 각 계좌를 업데이트하여 DB에 저장
        accountRepository.save(fromAccount);
        accountRepository.save(toAccount);
    }
}
  1. 출금 과정: fromAccount.withdraw(amount)가 호출되면, A 계좌에서 지정된 금액이 차감된다.
    2.. 입금 과정: toAccount.deposit(amount)가 호출되면, B 계좌에 금액이 입금된다.
  2. 트랜잭션 처리: @Transactional 어노테이션은 해당 메소드 내에서 모든 DB 연산을 하나의 트랜잭션으로 묶는다. 예를 들어, 출금이 성공했지만 입금 중에 오류가 발생하면, 트랜잭션 전체가 롤백되고, A 계좌에서 차감된 금액은 복구된다.

@Transactional의 동작 원리

  1. 트랜잭션 시작: @Transactional이 붙은 메서드가 호출되면, Spring은 트랜잭션을 시작한다. 이때, 트랜잭션 컨텍스트가 생성되어 해당 메서드 내에서 수행되는 모든 데이터베이스 작업이 이 컨텍스트 내에서 처리된다.
  2. 정상 종료 시 커밋: 메서드가 예외 없이 정상적으로 종료되면, 트랜잭션은 커밋되어 데이터베이스에 변경 사항이 영구적으로 반영된다.
  3. 예외 발생 시 롤백: 메서드 실행 중에 런타임 예외(예: NullPointerException, IllegalArgumentException)가 발생하면, Spring은 자동으로 트랜잭션을 롤백한다. 이는 데이터베이스 상태를 메서드 호출 이전 상태로 되돌리는 것을 의미한다.

예외를 잡는 경우: 메서드 내에서 발생한 예외를 try-catch 블록으로 잡아 처리하면 해당 예외는 트랜잭션의 롤백을 유발하지 않는다. 이 경우, 명시적으로 트랜잭션을 롤백하려면 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();를 호출해야 한다.

  • @Transactional은 주로 여러 쿼리문이 있을때 사용
  • 만약 쿼리문을 1개만 사용하면 굳이 사용하지 않거나 단순히 조회의 기능이라면, 읽기전용인 @Transactional(readOnly = true) 사용
profile
노력은 배신하지 않아 🔥

0개의 댓글