반응형

오늘은 Spring Boot에서 트랜잭션(Transaction)을 관리하는 방법에 대해 알아보겠습니다. 트랜잭션 관리는 데이터베이스의 일관성을 유지하고, 여러 작업이 하나의 작업처럼 처리되도록 보장합니다.


1. 트랜잭션의 기본 개념

(1) 트랜잭션이란?

트랜잭션은 데이터베이스에서 수행되는 하나의 논리적 작업 단위입니다. 트랜잭션의 주요 속성(ACID)은 다음과 같습니다.

  • Atomicity (원자성): 모든 작업이 성공하거나 실패합니다.
  • Consistency (일관성): 트랜잭션 전후로 데이터가 일관성을 유지합니다.
  • Isolation (고립성): 하나의 트랜잭션이 다른 트랜잭션의 영향을 받지 않습니다.
  • Durability (지속성): 트랜잭션이 성공적으로 완료되면 변경 내용이 영구적으로 저장됩니다.

(2) 트랜잭션 관리의 필요성

  • 데이터 무결성 보장
  • 실패 시 작업 롤백 지원
  • 동시성 문제 해결

2. Spring Boot에서 트랜잭션 설정

Spring Boot는 기본적으로 @Transactional 애노테이션을 통해 트랜잭션을 관리할 수 있습니다.

(1) 데이터베이스 설정

트랜잭션 관리 전에 데이터베이스 연결이 설정되어 있어야 합니다. (이전 강의 참조)

(2) @EnableTransactionManagement 활성화

Spring Boot에서는 자동으로 트랜잭션 관리가 활성화되지만, 명시적으로 사용하려면 @EnableTransactionManagement를 추가합니다.

import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableTransactionManagement
public class AppConfig {
    // 추가 설정이 필요한 경우 작성
}

3. @Transactional 애노테이션 사용

(1) 메서드 수준에서 트랜잭션 관리

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Transactional
    public void createUser(User user) {
        userRepository.save(user);

        // 테스트를 위해 예외를 강제로 발생시킴
        if (user.getName().equals("error")) {
            throw new RuntimeException("강제 예외 발생");
        }

        userRepository.save(new User("AnotherUser"));
    }
}

위 코드는 @Transactional 애노테이션을 사용하여 트랜잭션 범위를 지정합니다. 예외가 발생하면 모든 작업이 롤백됩니다.


4. 트랜잭션 전파와 격리 수준

(1) 전파 옵션 (Propagation)

Spring에서는 트랜잭션의 전파 방식을 설정할 수 있습니다. 주요 전파 옵션은 다음과 같습니다.

전파 속성 설명

REQUIRED 기본 옵션. 트랜잭션이 없으면 새 트랜잭션을 시작합니다.
REQUIRES_NEW 항상 새 트랜잭션을 생성합니다.
NESTED 기존 트랜잭션 내에서 중첩 트랜잭션을 시작합니다.
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void createAdditionalUser(User user) {
    userRepository.save(user);
}

(2) 격리 수준 (Isolation Level)

트랜잭션 격리 수준은 동시성 제어를 위한 설정입니다.

격리 수준 설명

READ_UNCOMMITTED 다른 트랜잭션의 커밋되지 않은 데이터도 읽을 수 있음
READ_COMMITTED 다른 트랜잭션이 커밋한 데이터만 읽을 수 있음
REPEATABLE_READ 동일 트랜잭션 내에서 동일한 데이터를 여러 번 읽어도 동일한 결과
SERIALIZABLE 가장 높은 격리 수준. 트랜잭션이 순차적으로 처리됨
@Transactional(isolation = Isolation.READ_COMMITTED)
public void performTransaction() {
    // 트랜잭션 내 작업
}

5. 테스트

(1) 성공 케이스

User user = new User("John");
userService.createUser(user);
// 데이터베이스에 두 명의 사용자("John", "AnotherUser")가 저장됩니다.

(2) 실패 케이스

User user = new User("error");
try {
    userService.createUser(user);
} catch (Exception e) {
    // 예외 발생으로 인해 모든 작업이 롤백됩니다.
}

6. 트랜잭션 관리 시 주의 사항

  1. 예외 처리:
    Spring은 RuntimeException과 Error만 트랜잭션을 롤백합니다.
    CheckedException에 대해 롤백하려면 명시적으로 설정해야 합니다.
  2. @Transactional(rollbackFor = Exception.class)
  3. @Transactional의 위치:
    @Transactional은 서비스 계층에 적용하는 것이 일반적입니다.
  4. Lazy Loading:
    트랜잭션이 종료되기 전에 연관 데이터를 로드해야 LazyInitializationException을 방지할 수 있습니다.
반응형

+ Recent posts