트랜잭션 이해하기

조경찬 (Jo Gyungchan)·2023년 9월 1일
0

spring

목록 보기
2/2

트랜잭션(Transaction)이란?


트랜잭션을 해석하면 거래라는 뜻인데, 트랜잭션은 데이터베이스에 실행한 로직을 안전하게 처리하도록 보장해준다.

예를 들어보자, 만약 A라는 사람이 B라는 사람에게 10000원을 이체해준다고 가정해보자. 이체가 성공적으로 마무리 되었다면 문제가 없겠지만 만약 A라는 사람이 송금은 완료되었는데 B라는 사람이 전달받을 때 문제가 생기게 된다면 A라는 사람의 돈만 10000원이 사라지게 된다.

이러한 문제를 방지하기 위해 사용하는 것이 트랜잭션이라는 기술이다.
트랜잭션을 사용해 계좌이체를 성공했다면 모든 작업을 정상 반영하도록 commit을 해주고, 작업 중간에 실패했다면 rollback하여 사용자들에게 문제가 없도록 해준다.

그래서 트랜잭션은 ACID라고 하는 것을 보장해야 한다.

  • 원자성(Atomicity)
  • 일관성(Consistency)
  • 격리성(Isolation)
  • 지속성(Durability)

트랜잭션 적용

트랜잭션은 먼저 비즈니스 로직이 있는 서비스 계층에서 시작해야 한다. 만약 비즈니스 로직이 잘못되면 롤백해야 하기 때문이다.

그럼 트랜잭션을 적용하기 위해 어떻게 해야할까?

  1. 트랜잭션을 적용하기 위해서는 커넥션이 필요하고, 비즈니스 로직에서는 같은 커넥션을 유지해야만 트랜잭션을 적용할 수 있다. 그래서 가장 원시적인 방법으로 같은 비즈니스 로직에서 수행하는 모든 메서드에 커넥션을 매개변수로 전달하는 방법이 있다.
public class Service {
    private final DataSource dataSource;
    private final Repository repository;

    public Service(DataSource dataSource, Repository repository) {
        this.dataSource = dataSource;
        this.repository = repository;
    }

    public void accountTransfer(String fromId, String toId, int money) throws SQLException {
 		// 같은 커넥션을 사용하도록 커넥션을 서비스 계층에서 생성
        Connection con = dataSource.getConnection();
        try {
            con.setAutoCommit(false);
            // 커넥션을 매개변수로 전달
            bizLogic(con, fromId, toId, money); 
            con.commit();
        } catch (Exception e) {
            con.rollback();
            throw new IllegalStateException(e);
        } finally {
            release(con);
        }
    }

    private void bizLogic(Connection con, String fromId, String toId, int money) throws SQLException {
        ...
    }

    private void release(Connection con) {
        if (con != null) {
            try {
                con.setAutoCommit(true);
                con.close();
            } catch (Exception e) {
                log.info("error", e);
            }
        }
    }
}
// Connection을 매개변수로 전달하면서 사용
public void method1(Connection connection, ...) { 
	run();
}
  • 하지만 이러한 방법은 계속 Connection을 전달해야 하는 번거로움도 있고,
    서비스 로직에서도 트랜잭션을 적용하기 위해 코드가 지저분해져서 잘 사용 하는 방법은 아니다.
  1. 두번째 방법은 트랜잭션 매니저와 트랜잭션 동기화 매니저를 통해 커넥션을 맺는 방법이다.

    동작 방식은

    • 트랜잭션 매니저가 데이터소스를 통해 미리 커넥션을 만들고 트랜잭션을 시작한다.
    transactionManager.getTransaction()
    • 트랜잭션 매니저가 트랜잭션이 시작된 커넥션을 트랜잭션 동기화 매니저에 보관한다.

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

    // 트랜잭션 동기화 매니저에 보관된 커넥션을 연결
    Connection connection =  DataSourceUtils.getConnection(dataSource); 
    
    //트랜잭션을 사용하기 위해 동기화된 커넥션을 닫지 않고 유지
    DataSourceUtils.releaseConnection(connection, dataSource)
    • 트랜잭션이 종료되면 트랜잭션 매니저는 트랜잭션 동기화 매니저에 보관된 커넥션을 통해 트랜잭션을 종료하고, 커넥션을 닫는다.
profile
한걸음씩 성장하는 개발자

0개의 댓글