Spring/주위주위

트랜잭션 주의

whyWhale 2022. 12. 23.

1. Inner Method에서의 동작

public class BooksImpl implements Books {

        public void addBooks(List<String> bookNames) {
                bookNames.forEach(bookName -> this.addBook(bookName));
        }

        @Transactional
        public void addBook(String bookName) {
                Book book = new Book(bookName);
                bookRepository.save(book);
                book.setFlag(true);
        }
}

Q. 해당 코드가 있고 addBook을 호출하는 Controller는 잘 호출될까?

A. 해당 코드에서 발생할 수 있는 문제는 @Transaction AOP가 적용되지 않는것이다.

why. 해당 @Transaction은 Spring에서 제공하는 AOP에 의해 동작되기 때문이다.

Spring AOP는 기본적으로 CGLIB라는 프록시를 사용한다.

CGLIB는 클래스 바이트 코드를 조작하여 프록시 객체를 생성하는 라이브러리 이다.

BookImpl을 호출하는 곳(caller)에서 BookImpl에 대한 메소드를 호출하면 해당 객체에 메소드로 접근하는 것이 아니기 때문이다.

caller쪽에서는 실제로 프록시를 통해 먼저 접근하게 되면서 트랜잭션 AOP가 위빙되고 실제 타겟 객체의 메소드를 호출한다.

위 코드는 proxy를 통한 호출이 아닌 직접적인 Target 클래스를 통해 접근하였기 때문에 @Transactional 애노테이션이 적용되지 않는 것이다.

2. Private Method에서의 동작

public class BooksImpl implements Books {

        @Transactional
        private void addBook(String bookName) {
                Book book = new Book(bookName);
                bookRepository.save(book);
                book.setFlag(true);
        }
}

Q. 해당 코드를 호출하는 클라이언트는 문제가 없을까?

A. 해당 코드에서도 @Transactional이 동작하지 않는다.

why. 해당 @Transaction은 Spring에서 제공하는 AOP에 의해 동작되기 때문이다.

프록시에의해 실질적인 BooksImpl 객체에 도달하는 과정을 거치는데 private 접근 지정 제어자라면 해당 BooksImpl을 참조하는 쪽에서 해당 메서드가 보이지 않기 때문이다.

댓글