Spring AOP관점에서 살펴보겠습니다.
AOP기능은 반드시 프록시 객체를 통해 메서드를 호출해야만 작동합니다.
이 때 Self-invocation이 발생할 경우, 프록시 객체가 아닌 진짜 자기 자신의 메서드를 호출하므로 AOP가 적용되지 않습니다.

@Service
public class UserService {
@Transactional
public void registerUser() {
saveUser(); // ← Self-invocation 발생!
}
@Transactional
public void saveUser() {
userRepository.save(...);
}
}
UserService클래스에 @Transactional을 사용한 두 메서드가 있습니다.
그러나 registerUser메서드 내부에서 saveUser메서드를 호출합니다.
즉 registerUser메서드가 프록시 객체를 거치지 않고 직접 saveUser메서드를 호출하였으므로, @Transactional어노테이션이 적용되지 않습니다.
따라서 saveUser메서드에서 예외가 발생하게 되더라도, 롤백이 되지 않습니다.
우리가 기대한것은 예외가 발생할 경우 유저가 등록되지 않고 롤백 되어야하는데, 유저 정보가 DB에 남고, 예외가 발생하게됩니다.
@Service
public class UserService {
@Transactional
public void registerUser() {
saveUser(); // ← Self-invocation 발생!
}
// ↓ 어노테이션 제거
public void saveUser() {
userRepository.save(...);
}
}
saveUser에서 @Transactional을 제거하면, 해당 메서드는 별도의 트랜잭션을 생성하지 않고,
이미 트랜잭션이 적용된 registerUser의 트랜잭션 범위 내에서 실행되므로 Self-invocation 문제가 발생하지 않습니다.
@Service
public class UserService {
private final UserInternalService internal;
@Transactional
public void registerUser() {
internal.saveUser(); // 프록시를 통해 호출됨
}
}
@Service
public class UserInternalService {
@Transactional
public void saveUser() {
// 트랜잭션 적용됨
}
}
위 코드와 같이 서비스를 분리할 경우

UserInternalService는 Spring에 의해 프록시로 감싸진 객체이며,
UserService에서 이를 주입받아 호출하면 프록시를 통해 saveUser가 실행되므로,
@Transactional이 정상적으로 적용됩니다.