300===Dev Framework/Spring

Spring @Transactional 완벽 가이드 🎯

블로글러 2024. 11. 14. 12:36

안녕하세요! 오늘은 Spring Framework에서 가장 중요한 기능 중 하나인 @Transactional에 대해 자세히 알아보겠습니다.

@Transactional이란? 🤔

트랜잭션이란 "all or nothing"의 원칙으로 작동하는 작업 단위입니다. 은행 송금을 예로 들면:

  • A 계좌에서 돈을 차감하고
  • B 계좌에 돈을 추가하는
    두 작업이 모두 성공하거나, 모두 실패해야 합니다.

@Transactional 애노테이션은 이러한 트랜잭션 처리를 간단하게 해주는 Spring의 마법 같은 기능입니다!

트랜잭션의 4가지 특성 (ACID) 💫

  1. 원자성 (Atomicity)

    • 트랜잭션 내 모든 작업은 모두 성공하거나 모두 실패
    • 중간 상태는 존재하지 않음
  2. 일관성 (Consistency)

    • 트랜잭션 전후로 데이터베이스는 일관된 상태를 유지
    • 예: 계좌 이체 후 총액은 변하지 않아야 함
  3. 격리성 (Isolation)

    • 동시에 실행되는 트랜잭션들은 서로 영향을 미치지 않음
    • 격리 수준을 통해 제어 가능
  4. 지속성 (Durability)

    • 성공적으로 완료된 트랜잭션은 영구적으로 반영
    • 시스템 장애가 발생해도 데이터는 보존

사용 방법 📝

1. 기본 설정

@Configuration
@EnableTransactionManagement
public class DatabaseConfig {

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

2. 서비스 계층에서 사용

@Service
public class UserService {

    @Transactional
    public void createUser(User user) {
        userRepository.save(user);
        emailService.sendWelcomeEmail(user);
        // 여기서 예외 발생시 모든 작업이 롤백됨!
    }

    @Transactional(readOnly = true)
    public User getUser(Long id) {
        return userRepository.findById(id);
    }
}

주요 속성 설명 ⚙️

  1. propagation (전파 옵션)

    @Transactional(propagation = Propagation.REQUIRED) // 기본값
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @Transactional(propagation = Propagation.SUPPORTS)
  2. isolation (격리 수준)

    @Transactional(isolation = Isolation.READ_COMMITTED) // 기본값
    @Transactional(isolation = Isolation.REPEATABLE_READ)
    @Transactional(isolation = Isolation.SERIALIZABLE)
  3. timeout (제한 시간)

    @Transactional(timeout = 10) // 10초
  4. readOnly (읽기 전용)

    @Transactional(readOnly = true) // 읽기 전용 최적화

실제 사용 예시 💡

1. 계좌 이체 서비스

@Service
public class BankService {

    @Transactional
    public void transfer(String fromAccount, String toAccount, BigDecimal amount) {
        Account from = accountRepository.findById(fromAccount);
        Account to = accountRepository.findById(toAccount);

        from.withdraw(amount);
        to.deposit(amount);

        accountRepository.save(from);
        accountRepository.save(to);
    }
}

2. 주문 처리 서비스

@Service
public class OrderService {

    @Transactional
    public Order createOrder(OrderRequest request) {
        // 재고 확인
        checkInventory(request);

        // 주문 생성
        Order order = new Order(request);
        orderRepository.save(order);

        // 재고 감소
        decreaseInventory(request);

        // 결제 처리
        processPayment(order);

        return order;
    }
}

주의사항 ⚠️

  1. private 메소드에는 적용되지 않음

    • @Transactional은 public 메소드에만 적용
  2. self-invocation 문제

    @Service
    public class UserService {
        public void method1() {
            method2(); // @Transactional 적용 안됨!
        }
    
        @Transactional
        public void method2() {
            // ...
        }
    }
  3. checked exception은 롤백되지 않음

    @Transactional(rollbackFor = Exception.class) // 모든 예외에 대해 롤백

성능 최적화 팁 🚀

  1. readOnly = true 활용

    • 읽기 전용 작업에는 꼭 설정
    • JPA/Hibernate 성능 최적화
  2. 적절한 격리 수준 선택

    • 기본값인 READ_COMMITTED가 적당
    • 필요한 경우에만 높은 격리 수준 사용
  3. 트랜잭션 범위 최소화

    • 필요한 작업만 트랜잭션 안에 포함
    • 긴 트랜잭션은 성능 저하의 원인

참고자료 📚

  1. Spring 공식 문서: https://docs.spring.io/spring-framework/reference/data-access/transaction.html
  2. Baeldung @Transactional 가이드: https://www.baeldung.com/transaction-configuration-with-jpa-and-spring
  3. Spring Blog: https://spring.io/blog/2019/05/16/transaction-management-and-spring
728x90