티스토리 뷰

728x90

Spring Framework에서 AOP(Aspect-Oriented Programming)는 강력한 트랜잭션 관리 기능을 제공합니다. 이 기능은 다양한 애플리케이션에서 트랜잭션을 효율적으로 관리할 수 있게 해주지만, 때때로 예상치 못한 문제가 발생하기도 합니다. 이 글에서는 Spring Data JPA를 사용하면서 자주 마주치는 self-invocation 문제와 그 배경에 대해 설명하고, Spring의 AOP가 어떻게 트랜잭션 관리에 적용되는지 구체적으로 살펴보겠습니다.

 

AOP와 트랜잭션 관리란?
Spring Framework에서 AOP(Aspect-Oriented Programming)는 다양한 곳에서 사용되는 로직을 한 곳에 모아 관리할 수 있게 해주는 프로그래밍 기법입니다. 예를 들어, 보안, 로깅, 트랜잭션 관리 등의 기능을 여러 곳에 적용해야 할 때 유용

특히, 트랜잭션 관리에서 AOP는 중요한 역할을 합니다. 데이터베이스 작업을 할 때, 작업의 시작부터 끝까지를 하나의 '트랜잭션'으로 묶어서 모든 작업이 성공적으로 완료되면 데이터베이스에 반영(commit)하고, 하나라도 실패하면 모든 변경사항을 취소(rollback)하는 기능을 말합니다.

Self-Invocation 문제
그런데 여기서 한 가지 문제가 발생할 수 있습니다. 바로 self-invocation 문제인데요, Self invocation은 동일한 클래스 내에서 한 메서드가 다른 메서드를 호출하는 것을 의미합니다. Spring의 AOP는 프록시(proxy)를 통해 동작하기 때문입니다. 그런데, self-invocation 상황에서는 이 프록시를 거치지 않고 직접 메소드를 호출하게 되므로, AOP를 통한 트랜잭션 관리가 적용되지 않아요. 아래 코드를 보면 updateUser 메서드 내에서 updateUserEmail 메서드를 호출하고 있죠? 이것이 바로 self invocation입니다.

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Transactional
    public void updateUser(Long userId, String name) {
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new IllegalArgumentException("User not found"));
        user.setName(name);
        userRepository.save(user);
        
        // Self invocation
        updateUserEmail(userId, "newemail@example.com");
    }

    @Transactional
    public void updateUserEmail(Long userId, String email) {
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new IllegalArgumentException("User not found"));
        user.setEmail(email);
        userRepository.save(user);
    }
}

Spring에서 트랜잭션 관리는 AOP(Aspect-Oriented Programming) 기반으로 동작합니다. @Transactional 애노테이션이 붙은 메서드가 호출되면, Spring은 해당 메서드를 프록시로 감싸서 트랜잭션을 적용하는데요. 문제는 self invocation 시에는 프록시를 거치지 않고 직접 호출되기 때문에 트랜잭션이 적용되지 않는다는 점입니다.

 

위 코드에서 updateUserEmail 메서드에는 @Transactional이 붙어있지만, updateUser 메서드에서 직접 호출하고 있기 때문에 트랜잭션이 적용되지 않습니다.

해결방법

첫 번째는 클래스 레벨에 @Transactional을 적용하는 것입니다. 이렇게 하면 클래스 내의 모든 메서드에 트랜잭션이 적용됩니다.

@Service
@Transactional
public class UserService {

    @Autowired
    private UserRepository userRepository;

    public void updateUser(Long userId, String name) {
        // ...
        updateUserEmail(userId, "newemail@example.com");
    }

    public void updateUserEmail(Long userId, String email) {
        // ...
    }
}

 

두 번째 방법은 프록시 객체를 직접 호출하는 것입니다. AopContext.currentProxy()를 사용하면 현재 프록시 객체를 얻을 수 있습니다.

@Service
@Transactional
public class UserService {

    @Autowired
    private UserRepository userRepository;

    public void updateUser(Long userId, String name) {
        // ...
        
        // AopContext를 사용하여 프록시 객체 호출
        ((UserService) AopContext.currentProxy()).updateUserEmail(userId, "newemail@example.com");
    }

    public void updateUserEmail(Long userId, String email) {
        // ...
    }
}

 

Self invocation을 사용할 때는 트랜잭션 전파(propagation)에 주의해야 합니다. 가능하면 self invocation을 피하고, 꼭 필요한 경우에는 클래스 레벨에 @Transactional을 적용하는 것이 안전합니다.

 

마무리

트랜잭션의 AOP는 외부 호출에 의해서만 실행된다는 것이 핵심이다.

Spring의 트랜잭션 관리는 프록시 기반의 AOP를 사용하기 때문에, 클라이언트가 프록시 객체를 통해 메서드를 호출할 때만 트랜잭션이 적용됩니다. 반면에 self invocation처럼 내부에서 메서드를 직접 호출하면 프록시를 거치지 않고 실제 객체의 메서드를 호출하기 때문에 트랜잭션이 적용되지 않는다.

 

"트랜잭션의 AOP는 외부 호출에 의해서만 실행된다. 내부 호출(self invocation)에는 트랜잭션이 적용되지 않으니 주의하자"

 

참고

chatGPT

https://stir.tistory.com/175

 

@Transactional 사용 시 자기 호출(Self-Invocation) 이슈 - 실습으로 배우는 JPA 3편

코드는 깃헙에 있습니다. 자기 호출 이슈는 보통 개발자가 @Transactional의 기능을 조작하려고 할 때 발생하는 이슈입니다. 그럼 우선 자기 호출이란 뭔지 알아봐야겠지요. 자기 호출이란? @Transactio

stir.tistory.com

https://woodcock.tistory.com/30

 

Spring AOP self-invocation이 발생하는 이유와 @Transaction 사용시 주의사항

최근 Spring AOP를 학습하는 도중 self-invocaion이라는 문제를 발견했습니다. 이 문제를 해결하면서 정리한 내용을 공유해보고자 합니다. public interface Pojo { void foo(); void bar(); } @Slf4j public class SimplePojo

woodcock.tistory.com

https://www.inflearn.com/questions/858567/transactional-%EC%97%90-%EB%94%B0%EB%A5%B8-%EB%B3%80%EA%B2%BD%EA%B0%90%EC%A7%80-%EC%9E%91%EB%8F%99%EC%97%AC%EB%B6%80-%EC%A7%88%EB%AC%B8

 

@Transactional 에 따른 변경감지 작동여부 질문 - 인프런

안녕하세요! 먼저 좋은 강의 감사하다는 말씀 드립니닷!개인 프로젝트 수행 중에, 변경 감지와 관련한 이슈를 발견해서 질문드립니다.물론 개인 프로젝트 관련한 질문은 안 받아주시는 것을 충

www.inflearn.com

 

728x90
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함