개발/Spring
@Transactional 내부에서 어떻게 동작할까?
yhyuk
2025. 6. 4. 17:35
728x90
반응형
Spring에서 @Transactional
은 트랜잭션을 시작하고, 성공하면 커밋하고, 실패하면 롤백하는 역할을 합니다.
내부적으로 어떻게 작동하는지 알아보자.
Proxy
- 비유: 트랜잭션은 '대리운전'이다
- 내가 평소처럼 운전하려고 시동을 걸었더니
- 대리운전 기사가 나 대신 운전대를 잡는다
- 원래 가려던 목적지까지 대신 안전하게 운전해준다
- 도착하면 비용 정산까지 알아서 처리한다
→ 이 대리운전 기사가 바로 Spring이 생성한 Proxy 객체이다.
내부 동작 구조
트랜잭션 흐름 구조
- @Transactional 이 붙은 클래스나 메서드 발견
- Spring 은 그 객체를 직접 생성하지 않고, Proxy 객체를 만들어 감싼다.
- 서비스 메서드 호출 시, 실제 객체가 아니라 Proxy가 먼저 호출을 가로챔
- 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를 거치지 않음 → 트랜잭션 동작 ❌- 즉, "대리운전"을 부르지않고 직접 운전해버린 꼴
해결방법
- inner() 를 다른 Bean으로 분리해서 호출
- 또는 아래와 같이 프록시를 직접 가져와서 호출
@Autowired
private ApplicationContext applicationContext;
public void outer() {
applicationContext.getBean(this.getClass()).inner(); // 프록시를 통해 호출
}
전체 흐름 정리
- @Transactional 발견 → Spring이 Proxy 객체 생성
- 메서드 호출 → Proxy가 트랜잭션 begin()
- 메서드 실행:
- 성공: commit()
- 예외: rollback()
- Proxy가 트랜잭션 책임을 끝까지 처리
마무리 요약
항목 | 설명 |
---|---|
역할 | 트랜잭션 경계 설정 (begin ~ commit/rollback) |
핵심 메커니즘 | AOP 기반의 Proxy가 메서드 호출을 가로채 트랜잭션 처리 |
주의사항 | 내부 메서드 호출은 Proxy를 통하지 않아 트랜잭션이 적용되지 않음 |
해결책 | 별도 Bean으로 분리하거나 applicationContext.getBean() 사용 |
728x90
반응형