web/Spring

@Transactional이 동작하지 않는 이유

반응형

개발을 elasticsearch, ddb, redis, mongo등을 사용하여 하다보면 가끔 rdb 자체에 transaction 기능이 너무 부러울 때가 있다. 

transaction은 '데이터베이스의 상태를 변화시키기 해서 수행하는 작업의 단위를 뜻한다' 사전적 의미와 같이 하나의 작업 단위의 묶음으로 작업이 실패하였을 때 롤백을 할 수 있어 데이터의 완결성을 지켜줄 수 있는 무기이다.

 

하지만 트랜잭션이 정상적으로 동작하지 못하는 경우가 있는데 이를 제대로 알고 사용하지 못하면 문제가 된다.

 

동작방식

우선 트랜잭션은 기본적인 동작방식은 AOP의 대표적인 사례라고 할 수 있듯이 AOP로 동작한다.

 

Aop는 핵심기능이 아닌 반복되는 부가적인 기능들을 핵심기능에서 벗어나서 더욱더 핵심기능이 객체지향적인 동작을 할 수있도록 해주는 기능인데 대표적으로 Transaction이다. 우리가 개발을 하면서 트랜잭션 확보를 위해서 메소드, 클래스레벨에 @Transactional을 붙여서 사용하는데 만약 이게 없다면 우리는 트랜잭션을 열고 커밋까지 할 수 있는 부가적인 코드를 무지하게 작성해야한다.

 

이 도움을 주는 @Transactional 메소드는 TrnasactionalInterceptor라는 advice에서 동작하게 되어있는데 이때 동작방식은 프록시 객체를 생성하여 동작할 수 있게 한다. 

 

 

그럼 어떤 경우에 트랜잭션이 사용될 수 없는가?

이 프록시 방식의 경우 기본적으로 인터페이스를 통해 타깃 오브젝트를 실행시킬 수 있도록 하는 기술이다. 그말은 타깃 오브젝트를 클라이언트(다른 오브젝트)에서 실행을 시켜야한다는 의미이다. 그렇기 때문에 내부 메소드로 부터 다른 내부 메소드를 호출하는 경우에는 이런 동작방식이 불가능하기 때문에 프록시 객체를 생성할 수 없어서 트랜잭션이 동작하지 않는다.

 

또다른 예로 애플리케이션이 동작하면서 로컬 캐시에 저장하기 위해 @Postconstruct를 사용하는 경우가 많은데 이때 또한 동일한 이유로 실행할 수 없다. 

 

물론 억지로 프록시 오브젝트를 가져와서 실제 오브젝트 메소드를 프록시를 이용하도록 강제하거나 AspectJ와 같이 바이트코드를 직접 조작하여 하는 방식등으로 강제할 수는 있지만 이는 스프링에서 원했던 기본 개념이 아니고 여러 문제를 야기할수도 있기때문에 권장하는 방식이 아니다.

 

저번글 (https://wedul.site/701)에 이어서 트랜잭션에 관한 내용을 정리했다. 항상 정상적으로 트랜잭션이 동작하는지 테스트해보고 많은 부분을 챙기면서 작업할 수 있도록 하자.

반응형