CS

@Transactional 은 어떻게 작동할까요?

wonow_ 2024. 12. 24. 16:07

개념

들어가기에 앞서


트랜잭션은 Spring의 대표적인 AOP 기술 중 하나입니다. 다른 프레임워크에선 이 @Transactional 기술을 부러워 하는 경우도 있습니다.

왜 그럴까요?

트랜잭션을 사용하지 않은 코드

public void saveUser(User user) {
    Connection connection = null;

    try {
        // 데이터베이스 커넥션을 얻음
        connection = dataSource.getConnection();

        // 트랜잭션 시작
        connection.setAutoCommit(false);

        // 사용자 저장 로직 실행
        PreparedStatement statement = connection.prepareStatement(
            "INSERT INTO users (username, email) VALUES (?, ?)"
        );
        statement.setString(1, user.getUsername());
        statement.setString(2, user.getEmail());
        statement.executeUpdate();

        // 트랜잭션 커밋
        connection.commit();
    } catch (SQLException e) {
        if (connection != null) {
            try {
                // 예외 발생 시 롤백
                connection.rollback();
            } catch (SQLException rollbackEx) {
                rollbackEx.printStackTrace();
            }
        }
        e.printStackTrace();
    } finally {
        if (connection != null) {
            try {
                // 커넥션 닫기
                connection.close();
            } catch (SQLException closeEx) {
                closeEx.printStackTrace();
            }
        }
    }
}

트랜잭션을 사용한 코드

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

@Transactional AOP 하나로 코드를 이렇게 줄일 수 있습니다.

 

 

@Transactional 의 숨겨진 기술: CGLib & JDK Proxy


  1. 직접 코드 수정 없이 트랜잭션 적용
  2. Spring 은 애플리케이션 코드에 직접적으로 트랜잭션 관련 로직(beginTransaction(), commit(), rollback()) 을 추가하지 않습니다. 대신, 프록시(Proxy) 객체를 생성하여 해당 로직을 자동으로 추가하여 실행합니다.
  3. Spring IoC Container 의 역할위의 예시로 UserService 클래스에 @Transactional 이 선언되어 있으면 UserService 대신 UserServiceProxy 라는 프록시 객체가 생성됩니다.
  4. Spring 은 @Transactional 이 선언된 클래스나 메서드를 감싸는 프록시 객체를 IoC 컨테이너에서 생성합니다.
  5. CGLib과 JDK Dynamic Proxy의 사용JDK Dynamic Proxy: 인터페이스를 기반으로 프록시 객체를 생성.
  6. CGLib: 상속을 통해 프록시 객체를 생성. (클래스 기반 프록시)

Spring은 기본적으로 인터페이스가 있을 경우 JDK Dynamic Proxy를, 인터페이스가 없을 경우 CGLib을 사용합니다.

  1. 트랜잭션 코드가 프록시 위에서 동작이렇게 하면 실제 메서드 코드는 변경하지 않아도 트랜잭션이 적용된 것처럼 동작합니다.
  2. 프록시 객체가 실제 메서드를 호출하기 전에 트랜잭션과 관련된 로직(시작, 커밋, 롤백)을 처리합니다.

 

Transactional Manager

결국 Proxy도 적용했고 Proxy가 Transaction도 관리하고 있습니다.

하지만 Proxy 가 Transaction 의 상태(begin, commit, rollback)를 스스로 알아서 관리하기엔 한계가 있습니다.

따라서 트랜잭션의 실제 상태를 관리하는 역할은 Transation Manager 에게 위임됩니다.

  • Spring에서 제공하는 PlatformTransactionManager대표적인 구현체로는 DataSourceTransactionManager가 있습니다.
  • Spring은 다양한 구현체를 제공하는 PlatformTransactionManager 인터페이스를 통해 트랜잭션을 관리합니다.
  • Transaction Manager의 역할
  • 트랜잭션 커밋(doCommit 메서드).
  • 트랜잭션 롤백 관리.
  • 트랜잭션 시작(doBegin 메서드).

Spring Config 에서 Transaction Manager 를 등록하는 예는 다음과 같습니다.

@Bean
public DataSource dataSource() {
    return new MysqlDataSource(); // 데이터베이스 연결
}

@Bean
public PlatformTransactionManager txManager() {
    return new DataSourceTransactionManager(dataSource()); // 트랜잭션 관리
}