Spring에서 @Transactional을 선언해도 private 메서드에는 트랜잭션이 동작하지 않는다. 그 이유와 원리를 함께 알아보자.
@Transactional은 Spring AOP 기반으로 동작한다.@Slf4j
@RequiredArgsConstructor
@Service
public class SelfInvocation {
private final MemberRepository memberRepository;
public void outerSaveWithPrivate(Member member) {
savePrivate(member); // 직접 호출 (프록시 우회)
}
public void outerSaveWithPublic(Member member) {
savePublic(member); // 직접 호출 (프록시 우회)
}
@Transactional
private void savePrivate(Member member) {
log.info("call savePrivate");
memberRepository.save(member);
throw new RuntimeException("rollback test");
}
@Transactional
public void savePublic(Member member) {
log.info("call savePublic");
memberRepository.save(member);
throw new RuntimeException("rollback test");
}
}
@SpringBootTest
class SelfInvocationTest {
@Autowired
private SelfInvocation selfInvocation;
@Autowired
private MemberRepository memberRepository;
@AfterEach
void tearDown() {
memberRepository.deleteAll();
}
@Test
void privateMethodTransactionFail() {
try {
selfInvocation.outerSaveWithPrivate(new Member("test"));
} catch (Exception e) {}
// 트랜잭션이 동작하지 않아 롤백되지 않음
assertThat(memberRepository.findAll()).hasSize(1);
}
@Test
void directPublicMethodTransactionSuccess() {
try {
selfInvocation.savePublic(new Member("test"));
} catch (Exception e) {}
// 프록시를 통해 호출된 public 메서드는 정상적으로 트랜잭션 적용됨
assertThat(memberRepository.findAll()).isEmpty();
}
}
결국, private 메서드는 AOP 프록시가 개입할 수 없어 트랜잭션이 적용되지 않는다.
this.메서드() 형식으로 호출하는 경우도 프록시를 우회하게 되어 트랜잭션이 적용되지 않는다.public void outer() {
this.innerTransactional(); // 트랜잭션 적용 안됨
}
@Transactional
public void innerTransactional() { ... }
@Service
public class InnerService {
@Transactional
public void txMethod() { ... }
}
@Service
public class OuterService {
private final InnerService innerService;
public void call() {
innerService.txMethod(); // 트랜잭션 정상 적용
}
}
public void outer() {
((MyService) AopContext.currentProxy()).inner();
}
@EnableAspectJAutoProxy(exposeProxy = true) 필요this 호출에도 AOP 적용 가능| 조건 | 트랜잭션 적용 여부 |
|---|---|
| public 메서드 + 외부 호출 | ✅ 적용됨 |
| public 메서드 + 내부 호출 (this.메서드()) | ❌ 적용 안됨 |
| private 메서드 | ❌ 적용 안됨 |
Spring AOP 기반의 @Transactional은 오직 프록시를 통한 public 메서드 호출에서만 작동한다.
프록시를 우회하는 private, this.메서드() 호출에서는 트랜잭션이 동작하지 않으므로 주의해야 한다.