오늘의 인기 글
최근 글
최근 댓글
Today
Total
01-02 02:44
관리 메뉴

우노

[Kotlin] @Transactional 본문

Web_App/Kotlin

[Kotlin] @Transactional

운호(Noah) 2024. 12. 23. 13:43

@Transactional 이란?

  • @Transactional은 Spring에서 트랜잭션 관리를 자동화해주는 강력한 애노테이션입니다.
  • 메서드나 클래스에 선언하면, 트랜잭션이 시작되고 작업 완료 시 자동으로 커밋하거나 예외 발생 시 롤백됩니다.

1. 메서드 수준에서 사용

  • 메서드가 실행되는 동안 트랜잭션이 시작됩니다.

      @Service
      class UserService(private val userRepository: UserRepository) {
    
          @Transactional
          fun createUser(user: User): User {
              userRepository.save(user) // 트랜잭션 내 작업
              return user // 트랜잭션 커밋
          }
      }

2. 클래스 수준에서 사용

  • 클래스에 선언하면 해당 클래스의 모든 메서드에 트랜잭션이 적용됩니다.

      @Service
      @Transactional
      class UserService(private val userRepository: UserRepository) {
    
          fun createUser(user: User): User {
              userRepository.save(user)
              return user
          }
    
          fun updateUser(user: User): User {
              userRepository.save(user)
              return user
          }
      }

주요 옵션

  • @Transactional은 다양한 옵션을 제공하여 트랜잭션 동작을 세밀하게 제어할 수 있습니다.

1. 전파 방식 (Propagation)

  • 트랜잭션이 호출될 때, 기존 트랜잭션을 사용할지, 새로 생성할지 결정합니다.

    • REQUIRED: 기본값. 기존 트랜잭션이 있으면 사용하고, 없으면 새로 생성합니다.

    • REQUIRES_NEW: 항상 새 트랜잭션을 생성하고, 기존 트랜잭션은 일시 중단됩니다.

    • SUPPORTS: 기존 트랜잭션이 있으면 사용하고, 없으면 트랜잭션 없이 실행됩니다.

    • NOT_SUPPORTED: 트랜잭션 없이 실행하고, 기존 트랜잭션이 있으면 일시 중단됩니다.

    • MANDATORY: 기존 트랜잭션이 반드시 있어야 하며, 없으면 예외가 발생합니다.

    • NEVER: 트랜잭션 없이 실행하고, 기존 트랜잭션이 있으면 예외가 발생합니다.

    • NESTED: 중첩 트랜잭션을 생성하며, 독립적인 롤백이 가능합니다. (JDBC에서만 지원)

      @Transactional(propagation = Propagation.REQUIRES_NEW)
      fun processPayment(payment: Payment) {
        // 항상 새로운 트랜잭션으로 실행
      }

2. 격리 수준 (Isolation)

  • 동시에 실행되는 트랜잭션 간 데이터 일관성을 유지하는 방법을 설정합니다.

    • DEFAULT: 데이터베이스의 기본 설정을 사용합니다. 일반적으로 READ_COMMITTED입니다.

    • READ_UNCOMMITTED: 커밋되지 않은 데이터도 읽을 수 있습니다. (Dirty Read 허용)

    • READ_COMMITTED: 커밋된 데이터만 읽을 수 있습니다. (Dirty Read 방지)

    • REPEATABLE_READ: 트랜잭션 동안 동일 데이터를 읽으면 항상 같은 값을 읽습니다. (Phantom Read 허용)

    • SERIALIZABLE: 가장 높은 격리 수준으로, 트랜잭션이 완전히 순차적으로 실행됩니다. (성능 저하 가능)

      @Transactional(isolation = Isolation.REPEATABLE_READ)
      fun getAccountBalance(accountId: Long): BigDecimal {
        // 트랜잭션 내 동일 데이터의 일관성 유지
      }

3. 읽기 전용 (ReadOnly)

  • 데이터 조회만 수행하는 경우, 최적화를 위해 읽기 전용으로 설정합니다.

      @Transactional(readOnly = true)
      fun getAllUsers(): List<User> {
          return userRepository.findAll() // 읽기 전용 작업
      }

4. 타임아웃 (Timeout)

  • 트랜잭션이 특정 시간 내 완료되지 않으면 롤백합니다.

      @Transactional(timeout = 5) // 5초 초과 시 롤백
      fun processLargeData() {
          // 오래 걸리는 작업
      }

5. 롤백 조건 (RollbackFor, NoRollbackFor)

  • 특정 예외에 대해 롤백 여부를 설정할 수 있습니다.

      @Transactional(rollbackFor = [CustomException::class])
      fun updateUser(user: User) {
          if (user.name.isEmpty()) {
              throw CustomException("Invalid user name") // 롤백 발생
          }
          userRepository.save(user)
      }

주의사항

  • 같은 클래스 내 메서드 호출

    • @Transactional은 Spring 프록시로 작동하므로, 같은 클래스 내 메서드 호출 시 적용되지 않습니다.

      @Transactional
      fun methodA() {
        methodB() // 트랜잭션 적용되지 않음
      }
  • Checked Exception

    • 기본적으로 Checked Exception이 발생해도 트랜잭션은 롤백되지 않습니다.
    • 롤백을 원하면 rollbackFor를 명시적으로 설정해야 합니다.

요약

  • @Transactional은 데이터의 일관성과 안정성을 보장하며, 트랜잭션 관리를 쉽게 해줍니다.
  • 주요 옵션(전파 방식, 격리 수준, 읽기 전용 등)을 상황에 맞게 설정하면, 성능과 유지보수성을 동시에 확보할 수 있습니다.
  • 기본 동작을 이해하고 적절히 설정하여 애플리케이션의 데이터 무결성을 유지하세요.
Comments