개발/Spring

@Transactional 내부에서 어떻게 동작할까?

yhyuk 2025. 6. 4. 17:35
728x90
반응형

Spring에서 @Transactional트랜잭션을 시작하고, 성공하면 커밋하고, 실패하면 롤백하는 역할을 합니다.
내부적으로 어떻게 작동하는지 알아보자.

Proxy

  • 비유: 트랜잭션은 '대리운전'이다
  • 내가 평소처럼 운전하려고 시동을 걸었더니
  • 대리운전 기사가 나 대신 운전대를 잡는다
  • 원래 가려던 목적지까지 대신 안전하게 운전해준다
  • 도착하면 비용 정산까지 알아서 처리한다

→ 이 대리운전 기사가 바로 Spring이 생성한 Proxy 객체이다.

내부 동작 구조

트랜잭션 흐름 구조

  1. @Transactional 이 붙은 클래스나 메서드 발견
  2. Spring 은 그 객체를 직접 생성하지 않고, Proxy 객체를 만들어 감싼다.
  3. 서비스 메서드 호출 시, 실제 객체가 아니라 Proxy가 먼저 호출을 가로챔
  4. Proxy 내부에서 다음 로직을 수행한다.
  • 트랜잭션 시작 (begin)
  • 원래 메서드 실행
    • 성공 시 commit(), 예외 발생 시 rollback()

예제 코드

@Transactional
public void transferMoney() {
// 1. 돈 출금
// 2. 돈 입금
}

정상 실행 시 흐름

Proxy: 트랜잭션 시작 (Connection begin)
    ↓
RealMethod: transferMoney()
    ↓
Proxy: 메서드 성공했네? commit()
    ↓
끝

예외 발생 시 흐름

Proxy: 트랜잭션 시작 (Connection begin)
    ↓
RealMethod: transferMoney()
    ↓
Exception 발생
    ↓
Proxy: rollback()

주의사항. 내부 메서드 호출 시 트랜잭션 안 걸린다?

public void outer() {
  inner(); // @Transactional이 있어도 트랜잭션 적용 X
}

@Transactional
public void inner() {
  // 트랜잭션이 적용되지 않음!
}

이유

  • inner() 메서드는 같은 클래스 내부에서 직접 호출되므로 Proxy를 거치지 않음 → 트랜잭션 동작 ❌
  • 즉, "대리운전"을 부르지않고 직접 운전해버린 꼴

해결방법

  1. inner() 를 다른 Bean으로 분리해서 호출
  2. 또는 아래와 같이 프록시를 직접 가져와서 호출
@Autowired 
private ApplicationContext applicationContext;

public void outer() { 
    applicationContext.getBean(this.getClass()).inner(); // 프록시를 통해 호출 
}

전체 흐름 정리

  1. @Transactional 발견 → Spring이 Proxy 객체 생성
  2. 메서드 호출 → Proxy가 트랜잭션 begin()
  3. 메서드 실행:
    • 성공: commit()
    • 예외: rollback()
  4. Proxy가 트랜잭션 책임을 끝까지 처리

마무리 요약

항목 설명
역할 트랜잭션 경계 설정 (begin ~ commit/rollback)
핵심 메커니즘 AOP 기반의 Proxy가 메서드 호출을 가로채 트랜잭션 처리
주의사항 내부 메서드 호출은 Proxy를 통하지 않아 트랜잭션이 적용되지 않음
해결책 별도 Bean으로 분리하거나 applicationContext.getBean() 사용
728x90
반응형