Spring Self-invocation

김현찬·2025년 6월 24일

Self-invocation이란?

  • 자기 자신의 메서드를 호출하는 것입니다.

그게 왜 문제일까?

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에 남고, 예외가 발생하게됩니다.

해결 방법

1.

  @Service
public class UserService {

    @Transactional
    public void registerUser() {
        saveUser(); // ← Self-invocation 발생!
    }

//  ↓ 어노테이션 제거
    public void saveUser() {
        userRepository.save(...);
    }
}

saveUser에서 @Transactional을 제거하면, 해당 메서드는 별도의 트랜잭션을 생성하지 않고,
이미 트랜잭션이 적용된 registerUser의 트랜잭션 범위 내에서 실행되므로 Self-invocation 문제가 발생하지 않습니다.

2.

@Service
public class UserService {
    private final UserInternalService internal;
	
    @Transactional
    public void registerUser() {
        internal.saveUser();  // 프록시를 통해 호출됨
    }
}
@Service
public class UserInternalService {

    @Transactional
    public void saveUser() {
        // 트랜잭션 적용됨
    }
}

위 코드와 같이 서비스를 분리할 경우

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

0개의 댓글