본문 바로가기
Spring

[Spring+DB] 트랜잭션 추상화 및 동기화- TransactionManager

by 개미가되고싶은사람 2024. 8. 10.

목차

     

    1. 트랜잭션 코드를 추상화하지 않으면?

    트랜잭션 관련 코드를 추상화하지 않으면 코드의 유지보수성과 확장성이 저하될 수 있습니다. 예를 들어, 기존에 JDBC를 사용하고 있었는데 JPA로 교체해야 하는 상황이 발생할 수 있습니다. 이 경우, 데이터 접근 계층뿐만 아니라 서비스 계층에서도 코드 변경이 필요합니다. 이는 단일 책임 원칙(SRP)과 개방-폐쇄 원칙(OCP)을 위반하게 됩니다.

    이 문제를 해결하기 위해 트랜잭션 관련 코드를 추상화하는 것이 좋습니다. 이를 통해 코드가 단일 책임 원칙을 따르고, 변경에 더 잘 대응할 수 있게 됩니다. Spring에서는 트랜잭션 추상화 관련 코드를 TransactionManager 인터페이스를 통해서 제공합니다.

     

    2. TransactionManager란?

    Spring에서 TransactionManager는 애플리케이션 내의 트랜잭션의 일부가 실패하면 전체 트랜잭션이 롤백되어 데이터가 일관된 상태로 유지시키거나 성공하면 데이터을 커밋하게 도와주는 인터페이스입니다. 

     

    TransactionManager 계층 구조

    TransactionManager
    └── PlatformTransactionManager
        └── AbstractPlatformTransactionManager
            ├── JpaTransactionManager
            ├── DataSourceTransactionManager
            ├── HibernateTransactionManager
            └── RedisTransactionManager

     

     

    2-1 TransactionManager 역할과 기능

    스프링 제공하는 트랜잭션 매니저는 크게 2가지 역할을 합니다.

     

    1. 트랜잭션 동기화

    트랜잭션 추상화는 개발자가 트랜잭션 추상화 코드를 작성하지 않고 트랜잭션 코드를 추상화 할 수 있도록 해주는 기능입니다. 스프링은 다양한 트랜잭션 인터페이스 코드(예: JDBC, Hibernate 등)와 연동될 수 있도록 추상화 계층을 제공합니다. 스프링의 트랜잭션 매니저를 통해 이러한 다양한 트랜잭션 인터페이스 코드를 간단하게 사용할 수 있습니다.

     

    2. 리소스 동기화

    리소스 동기화는 트랜잭션 내에서 사용되는 여러 리소스(예: 데이터베이스 커넥션 등)를 일관되게 관리하는 것을 의미합니다. 트랜잭션이 시작되면 스프링의 트랜잭션 관리자는 필요한 리소스를 확보하고 이 리소스를 트랜잭션의 생애 주기 동안 유지합니다. 그리고 트랜잭션이 완료되면 이 리소스들을 적절하게 커밋(commit)하거나 롤백(rollback)합니다.

    이를 통해 동일한 트랜잭션 내에서 여러 리소스가 일관성 있게 처리할 수 있습니다.

     

     

    2-2 TransactionManager동작 방식

    동작 방식을 간단하게 설명하면 다음과 같습니다.

    1. 트랜잭션 매니저는 DataSource를 통해 커넥션을 만들고 트랜잭션을 시작한다.

    2. 트랜잭션 매니저는 트랜잭션이 시작된 커넥션을 트랜잭션 동기화 매니저에 보관한다.

    3. 리포지토리는 트랜잭션 동기화 매니저에 보관된 커넥션을 꺼내서 사용한다. 따라서 파라미터로 커넥션을 전달하 지 않아도 된다.

    4. 트랜잭션이 종료되면 트랜잭션 매니저는 트랜잭션 동기화 매니저에 보관된 커넥션을 통해 트랜잭션을 종료하고, 커넥션도 닫는다.

     

    ※ 트랜잭션 동기화 매니저에 저장되는 커넥션은 각각의 쓰레드마다 별도의 저장소를 부여받습니다. 이로 인해 각 쓰레드는 자신에게 할당된 커넥션을 통해 데이터에 접근할 수 있으며, 동시에 여러 쓰레드가 동일한 커넥션을 사용하지 않도록 보장됩니다. 따라서 데이터 접근 시 동시성 문제를 방지할 수 있습니다.

     

     

    2-3 TransactionManager(트랜잭션 동기화 매니저) 간단한 구현 

        private void close(Connection con, Statement stmt, ResultSet rs) {
            JdbcUtils.closeResultSet(rs);
            JdbcUtils.closeStatement(stmt);
            DataSourceUtils.releaseConnection(con, dataSource);
        }
    
        private Connection getConnection() throws SQLException {
            Connection con = DataSourceUtils.getConnection(dataSource);
            log.info("get connection={}, class={}", con, con.getClass());
            return con;
        }
    • getConnection(): getConnection 메서드를 호출하면, 먼저 현재 쓰레드에 바인딩된 트랜잭션이 있는지 확인합니다.

    트랜잭션 있는 경우

    만약 현재 쓰레드에 트랜잭션이 활성화되어 있다면, DataSourceUtils는 해당 트랜잭션에 연결된 커넥션을 반환합니다. 

     

    트랜잭션이 없는 경우

    현재 쓰레드에 트랜잭션이 없다면, DataSourceUtils는 DataSource를 사용하여 새로운 커넥션을 생성합니다.

     

    • releaseConnection(): 현재 쓰레드 트랜잭션에 상태를 확인한 뒤 커넥션을 유지하거나 닫습니다.

     

    참고

    스프링 DB 1편 - 데이터 접근 핵심 원리(김영한)