[Spring] Transaction의 동작 원리

슈퍼대디·2024년 12월 24일

CS면접대비

목록 보기
11/13

Spring Transaction의 동작 원리

목차

  1. 트랜잭션 기본 개념
  2. Spring 트랜잭션 처리 방식
  3. 트랜잭션 전파와 격리 수준
  4. 트랜잭션 동작의 내부 원리
  5. 면접 예상 질문

1. 트랜잭션 기본 개념

ACID 속성

  • Atomicity (원자성): 트랜잭션의 연산은 모두 실행되거나 전혀 실행되지 않아야 함
  • Consistency (일관성): 트랜잭션 실행 전후의 데이터베이스는 일관된 상태를 유지해야 함
  • Isolation (격리성): 동시에 실행되는 트랜잭션들은 서로 영향을 미치지 않아야 함
  • Durability (지속성): 성공적으로 완료된 트랜잭션의 결과는 영구적으로 반영되어야 함

Spring이 제공하는 트랜잭션 추상화

public interface PlatformTransactionManager {
    // 트랜잭션 시작
    TransactionStatus getTransaction(TransactionDefinition definition);
    
    // 트랜잭션 커밋
    void commit(TransactionStatus status);
    
    // 트랜잭션 롤백
    void rollback(TransactionStatus status);
}

2. Spring 트랜잭션 처리 방식

선언적 트랜잭션 (@Transactional)

@Service
public class UserService {
    @Transactional
    public void createUser(User user) {
        // 이 메서드는 트랜잭션으로 처리됨
        userRepository.save(user);
        emailService.sendWelcomeEmail(user);
    }
}

프로그래매틱 트랜잭션

@Service
public class UserService {
    private final PlatformTransactionManager transactionManager;
    
    public void createUser(User user) {
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        try {
            userRepository.save(user);
            emailService.sendWelcomeEmail(user);
            transactionManager.commit(status);
        } catch (Exception e) {
            transactionManager.rollback(status);
            throw e;
        }
    }
}

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

트랜잭션 전파 (Propagation)

@Service
public class OrderService {
    @Transactional(propagation = Propagation.REQUIRED)
    public void createOrder(Order order) {
        // 기본값: 트랜잭션이 없으면 새로 생성, 있으면 참여
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void processPayment(Payment payment) {
        // 항상 새로운 트랜잭션 생성
    }
    
    @Transactional(propagation = Propagation.SUPPORTS)
    public Order findOrder(Long id) {
        // 트랜잭션이 있으면 참여, 없으면 트랜잭션 없이 실행
    }
}

트랜잭션 전파 속성

  1. REQUIRED (기본값)

    • 트랜잭션이 없으면 새로 생성
    • 있으면 기존 트랜잭션에 참여
  2. REQUIRES_NEW

    • 항상 새로운 트랜잭션 생성
    • 기존 트랜잭션은 일시 중단
  3. SUPPORTS

    • 트랜잭션이 있으면 참여
    • 없어도 실행
  4. MANDATORY

    • 트랜잭션이 반드시 있어야 함
    • 없으면 예외 발생
  5. NEVER

    • 트랜잭션이 있으면 예외 발생
    • 트랜잭션 없이 실행
  6. NOT_SUPPORTED

    • 트랜잭션 없이 실행
    • 기존 트랜잭션은 일시 중단

트랜잭션 격리 수준

@Transactional(isolation = Isolation.READ_COMMITTED)
public void processOrder(Order order) {
    // READ_COMMITTED 격리 수준으로 실행
}

상세 내용은 아래 링크글 참고 부탁드립니다.
트랜잭션 격리수준의 이해

4. 트랜잭션 동작의 내부 원리

프록시 기반의 AOP

// 실제 생성되는 프록시의 동작 방식
public class UserServiceProxy extends UserService {
    private final PlatformTransactionManager transactionManager;
    private final UserService target;
    
    @Override
    public void createUser(User user) {
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        try {
            target.createUser(user); // 실제 메서드 호출
            transactionManager.commit(status);
        } catch (Exception e) {
            transactionManager.rollback(status);
            throw e;
        }
    }
}

트랜잭션 매니저 동작 과정

  1. 트랜잭션 시작
    • DataSourceTransactionManager가 Connection 획득
    • Connection의 auto-commit을 false로 설정
  2. 트랜잭션 커밋/롤백
    • 정상 완료 시 Connection.commit() 호출
    • 예외 발생 시 Connection.rollback() 호출
  3. 트랜잭션 정리
    • Connection을 커넥션 풀에 반환

트랜잭션 동기화 매니저

public class TransactionSynchronizationManager {
    private static final ThreadLocal<Map<Object, Object>> resources = new ThreadLocal<>();
    
    // 트랜잭션 리소스(Connection) 바인딩
    public static void bindResource(Object key, Object value) {
        Map<Object, Object> map = resources.get();
        if (map == null) {
            map = new HashMap<>();
            resources.set(map);
        }
        map.put(key, value);
    }
    
    // 트랜잭션 리소스 조회
    public static Object getResource(Object key) {
        Map<Object, Object> map = resources.get();
        if (map == null) {
            return null;
        }
        return map.get(key);
    }
}

5. 면접 예상 질문

Q: @Transactional이 동작하는 원리에 대해 설명해주세요.
A: @Transactional은 Spring AOP를 기반으로 동작합니다. Spring은 @Transactional이 붙은 클래스나 메서드에 대해 프록시를 생성합니다. 이 프록시는 실제 메서드 호출 전후에 트랜잭션 관련 로직을 추가하여 실행합니다. 구체적으로는 트랜잭션 시작, 커밋, 롤백 등의 처리를 담당하며, TransactionManager를 통해 실제 트랜잭션을 관리합니다. 또한 ThreadLocal을 사용하여 트랜잭션 정보를 전파하고 관리합니다.

Q: 트랜잭션 전파 속성 중 REQUIRED와 REQUIRES_NEW의 차이점은 무엇인가요?
A: REQUIRED와 REQUIRES_NEW는 다음과 같은 차이가 있습니다:

  • REQUIRED는 이미 진행 중인 트랜잭션이 있다면 그 트랜잭션에 참여하고, 없다면 새로운 트랜잭션을 시작합니다. 이는 가장 일반적인 전파 속성으로, 대부분의 경우에 적합합니다.
  • REQUIRES_NEW는 항상 새로운 트랜잭션을 시작합니다. 이미 진행 중인 트랜잭션이 있다면 그 트랜잭션을 잠시 중단하고 새로운 트랜잭션을 시작합니다. 이는 독립적인 트랜잭션이 필요한 경우, 예를 들어 로깅이나 감사(audit) 기록 등에 유용합니다.

Q: 스프링의 트랜잭션 추상화가 제공하는 이점은 무엇인가요?
A: 스프링의 트랜잭션 추상화는 다음과 같은 이점을 제공합니다:
1. 데이터 액세스 기술에 독립적인 코드 작성 가능 (JPA, JDBC, Hibernate 등)
2. 선언적 트랜잭션과 프로그래밍 방식 트랜잭션을 모두 지원
3. 다양한 트랜잭션 매니저를 일관된 방식으로 사용 가능
4. 트랜잭션 전파와 격리 수준을 쉽게 제어 가능
이러한 추상화를 통해 개발자는 실제 트랜잭션 처리 방식에 구애받지 않고 비즈니스 로직에 집중할 수 있습니다.

참고 자료

profile
성장하고싶은 Backend 개발자

0개의 댓글