본문 바로가기
Spring/Spring JPA

스프링 트랜잭션 추상화, 동기화

코동이 2022. 11. 30.

개요


스프링 트랜잭션의 추상화, 동기화를 이해할 수 있다.

 

 

트랜잭션 추상화


 JDBC를 사용하다가 JPA로 기술을 변경하면 어떻게 될까요? JDBC 종속적인 수많은 코드들을 JPA로 변경해야 합니다. 그러다가 JPA가 또 다른 기술로 변경되면 어떻게 될까요? 해당 기술에 맞춰서 수많은 변경을 해야 합니다. 

 

아래는 트랜잭션을 사용하는 JDBC 코드와 JPA 코드 비교입니다. 라이브러리가 완전히 바뀝니다.

 

//JDBC
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);
  }
}


//JPA
public static void main(String[] args) {
  //엔티티 매니저 팩토리 생성
  EntityManagerFactory emf =
  Persistence.createEntityManagerFactory("jpabook");
  EntityManager em = emf.createEntityManager(); //엔티티 매니저 생성
  EntityTransaction tx = em.getTransaction(); //트랜잭션 기능 획득
  try {
    tx.begin(); //트랜잭션 시작
    logic(em); //비즈니스 로직
    tx.commit();//트랜잭션 커밋
  } catch (Exception e) {
    tx.rollback(); //트랜잭션 롤백
  } finally {
    em.close(); //엔티티 매니저 종료
  }
  emf.close(); //엔티티 매니저 팩토리 종료
}

 

 아래 그림과 같이 @Service는 데이터 접근 기술에 직접 의존하고 있습니다. 하지만 이렇게 특정 기술에 의존하는 코드는 바람직하지 않습니다. 트랜잭션에 접근하는 기술들은 모두 다른 코드를 사용하기 때문에 기술이 바뀔때마다 엄청난 코드 수정을 해야 하기 때문입니다.

 

 

 따라서, 어떤 기술을 사용하든지 @Service 쪽에서 최대한 핵심 비지니스 코드 변경이 없도록 추상화 기술을 사용해 이 문제를 해결합니다. 추상화 기술을 통한 새로운 구조는 아래와 같습니다.

 

 

 스프링에서 추상화의 핵심은 PlatformTransactionManager입니다. 이 인터페이스 덕분에 JDBC, JPA, Hibernate 등의 DB 기술들을 편하게 사용할 수 있습니다. @Service가 인터페이스에 의존하기 때문에 인터페이스의 구현체만 변경하여 @Service 비지니스 로직 변화 없이 기술을 쉽게 변경하고 코드 변경을 예방할 수 있습니다.

 

 PlatformTransactionManager 인터페이스의 3개 메서드는 아래와 같습니다. 트랜잭션을 시작하는 getTransaction()부터 commit(), rollback()이 있으므로 기술에 따라서 적절한 구현체가 사용됩니다. 

 

package org.springframework.transaction;

public interface PlatformTransactionManager extends TransactionManager {
	TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
	void commit(TransactionStatus status) throws TransactionException;
	void rollback(TransactionStatus status) throws TransactionException;
}

 

 

트랜잭션 동기화


 스프링은 트랜잭션 매니저 안에 있는 트래잭션 동기화 매니저의 쓰레드로컬(ThreadLocal)을 사용해 커넥션을 동기화합니다. 아래 그림은 DataSourceTransactionManager를 기준으로 비지니스 로직에서 트랜잭션 작동 과정을 트랜잭션 동기화 매니저 중심으로 표현한 것입니다.

 

트랜잭션 시작

트랜잭션이 시작되면 진행 흐름은 다음과 같습니다.

 

1. transactionManager.getTransaction()으로 트랜잭션을 시작합니다.

2. 트랜잭션 매니저가 데이터 소스에서 커넥션을 만듭니다.

3. con.setAutoCommit(false)로 변경합니다.

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

5. 트랜잭션 동기화 매니저는 쓰레드로컬(ThreadLocal) 덕분에 멀티 쓰레드 환경에서 쓰레드를 안전하게 보관합니다.

 

 

비지니스 로직 수행

 

6. 비지니스 로직에서 데이터 접근 로직을 호출합니다.

7. DataSourceUtils.getConnection(dataSource)로 트랜잭션 동기화 매니저에 있는 커넥션을 조회합니다.

8. SQL을 실행합니다.

 

트랜잭션 종료

 

9. 비지니스 로직이 끝나고 transactionManager.commit() 혹은 rollback()으로 트랜잭션을 종료합니다.

10. 트랜잭션 동기화 매니저에 있는 커넥션을 획득합니다.

11. 획득한 커넥션으로 con.commit() 혹은 con.rollback()을 합니다.

12. 전체 리소스를 정리합니다.(con.setAutoCommit(true), con.close() 수행)

 

 

트랜잭션 동기화 매니저는 아래와 같이 쓰레드 로컬(ThreadLocal)을 사용하여 쓰레드 독립적으로 커넥션을 동기화시킵니다.

 

 

PlatformTransactionManager를 직접 사용하던 것에서, 템플릿 콜백 패턴으로 TransactionTemplate을 사용하면, 코드의 중복과 양을 더욱 줄일 수 있습니다.

 

 

*참고

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

반응형

'Spring > Spring JPA' 카테고리의 다른 글

트랜잭션 전파 전략 7개  (0) 2022.12.03
스프링 트랜잭션 전파 기본개념  (0) 2022.11.17
OSIV  (0) 2022.10.01
일반 Join vs Fetch Join  (0) 2022.06.29
JPA를 이용해 History 테이블 자동 생성하기  (1) 2021.12.14