@Transactional 이해

Yangray·2025년 4월 1일

[출처] 김영한 - 데이터 접근 활용 기술 강의

@Transactional

  • 스프링이 제공하는 트랜잭션 AOP로 선언적 트랜잭션 관리 방법
  • 프록시 방식의 AOP 적용

프록시 도입 전
트랜잭션을 처리하기 위한 프록시를 도입하기 전에는 서비스의 로직에서 트랜잭션을 직접 시작했다.

//트랜잭션 시작
  TransactionStatus status = transactionManager.getTransaction(new 
  DefaultTransactionDefinition());
  try {
    //비즈니스 로직
    bizLogic(fromId, toId, money);
    transactionManager.commit(status); //성공시 커밋 
  } catch (Exception e) {
      transactionManager.rollback(status); //실패시 롤백
  throw new IllegalStateException(e); 
  }

프록시 도입 후
트랜잭션을 처리하는 객체와 비즈니스 로직 처리하는 서비스 객체를 명확하게 분리할 수 있다.

public class Service {
  public void logic() {
  //트랜잭션 관련 코드 제거, 순수 비즈니스 로직만 남음 
  bizLogic(fromId, toId, money);
  } 
}

@Transactional이 특정 클래스나 메서드에 하나라도 존재하면 트랜잭션AOP는 프록시를 생성 후 스프링 컨테이너에 등록한다.
그리고 실제 service 객체 대신에 프록시를 스프링 빈에 등록하고, 프록시는 내부에 실제 service 객체를 참조하게 된다.

프록시는 실제 객체 service를 상속해서 만들어지기 때문에 다형성을 활용할 수 있다.
따라서 BasicService 대신에 프록시를 주입할 수 있다.

트랜잭션AOP 주의 사항

1. 프록시 내부 호출

  • AOP를 적용하면 스프링은 대상 객체 대신에 프록시를 스프링 빈으로 등록한다.
    따라서 의존관계 주입시에 항상 실제 객체 대신에 프록시 객체를 주입하기 때문에 실제 객체를 직접 호출하는 문제는 일반적으로 발생 빈도가 적다. 하지만 실제 객체의 내부에서 메서드 호출이 발생하면 프록시를 거치지 않고 실제 객체를 직접 호출하는 문제가 발생한다.
    이렇게 되면 @Transactional이 있어도 트랜잭션이 적용이 되지 않는다.

자바에서 별도의 참조가 없으면 this라는 뜻으로 자기 자신의 인스턴스를 가리킨다.
따라서 this.internal()이 실행이 되고, this는 자기 자신을 가리키므로 실제 객체 인스턴스를 의미한다. 따라서 트랜잭션을 적용할 수 없다.

해결방법

메서드 내부 호출 때문에 트랜잭션 프록시가 적용되지 않는 문제를 해결하기 위해 internal() 메서드를 별도의 클래스로 분리

2. 초기화 시점

  • 스프링 초기화 시점에는 트랜잭션AOP가 적용되지 않을 수 있다.
    초기화가 먼저 호출되고 트랜잭션AOP가 적용되기 때문에 초기화 시점에는 해당 메서드에서 트랜잭션을 획득할 수 없다.
profile
시작은 미약하나 그 끝은 창대하리라

0개의 댓글