본문 바로가기

CS 잡지식

@Transational 사용 시 주의점( 매우 중요 && 면접에서 자주 나옴)

 

 

@SpringBootTest
public class InternalCallV1Test {

 @Autowired
 CallService callService;
 
 @Test
 void printProxy() {
 log.info("callService class={}", callService.getClass());
 }
 
 @Test
 void internalCall() {  // 정상 실행됨!
 callService.internal();
 }
 
 @Test
 void externalCall() { // external() 내부의 inernal()이 트랜잭션 적용을 받지 못함!!
 
 callService.external();
 
 }
 
 @TestConfiguration
 static class InternalCallV1Config {
 
 @Bean
 CallService callService() {
 return new CallService();
 }
 
 }
 
 
 @Slf4j
 static class CallService {
 
 public void external() {
 
 log.info("call external");
 printTxInfo();
 internal();
 }
 
 
 @Transactional
 public void internal() {
 
 log.info("call internal");
 printTxInfo();
 
 }
 
 
 
 private void printTxInfo() {
 boolean txActive =
TransactionSynchronizationManager.isActualTransactionActive();
 log.info("tx active={}", txActive);
 }
 }
}


@Transational 메서드가 적용되지 않는 메서드(A 메서드)에서, 외부 메서드(@Transactional가 붙은 B 메서드)를 호출하면 안 된다.

왜냐하면, B 메서드는 프록시 객체를 통해서만 호출이 된다.

만약 A 메서드를 실행을 하게 되면, A 메서드는 프록시 객체를 거치지 않고, 실제 객체를 직접 참조하여 실행을 하게 된다.

그렇게 되면, A 메서드 내부의 Transaction이 적용되야 하는 B 메서드가 프록시 객체를 통해서 실행되지 않기 때문에 

트랜잭션이 적용되지 않는 심각한 문제가 발생을 한다. 


Q. @Transactional이 붙지 않은 메서드는 왜 실제 객체를 직접 참조하여 실행을 하는가??

A. 스프링 트랜잭션 AOP는 실제 객체에서 @Transational이 붙어 있는 메서드만을 오버 라이딩을 하여 구현된다.

고로, 애노테이션이 없는 메서드의 경우, 프록시를 통해서 호출을 할 수가 없기 때문이다.

반대로, @Transactional이 붙어 있는 메서드는 무조건 프록시만을 통해서 트랜잭션 적용을 받는다. 

 

위 사진에도 나와 있듯이, internal() 호출 시, this(실제 객체)로 호출이 되고 있다.

위에서도 언급을 하였듯이, 스프링은 프록시를 통해서만 트랜잭션을 적용 받기 때문에 this로 internal()를 호출하게 트랜잭

션이 적용되지 않는다. 

해결 방법 : 내부 호출을 피하기 위해, internal() 메서드를 별도의 클래스로 분리!!

( 트랜잭션 AOP 주의 사항 - 프록시 내부 호출2  참조!)

'CS 잡지식' 카테고리의 다른 글

동기화(Synchronization) VS 동시성(Concurrency)  (0) 2023.02.18
HTML vs CSS  (0) 2023.02.18
HTML, CSS, Java Script에 대한 고찰!  (0) 2023.02.18
예외는 2가지 측면에서 생각!  (0) 2023.02.13
SQL 작성 TIP  (0) 2022.12.08