Skip to content

김준기 6주차 학습 일지

June edited this page Aug 6, 2024 · 2 revisions

스프링 트랜잭션 내용을 학습해야할 필요가 있어서, 6주차에는 해당 내용 위주로 학습을 진행했습니다.

트랜잭션 적용 확인

@Transactional AOP가 제대로 적용되고 있는지 확인하는 방법을 알아본다.

TransactionSynchronizationManager 추상 클래스의 isActualTransactionActive() 메서드로 트랜잭션 적용 여부를 확인할 수 있다. 현재 스레드에 트랜잭션이 적용되어 있는지 확인할 수 있는 기능이다.

@Slf4j
@Service
public class BasicService {

  @Transactional
  public void tx() {
    log.info("call tx");
    boolean txActive = TransactionSynchronizationManager.isActualTransactionActive();
    log.info("tx active = {}", txActive);
  }
}
@Test
@DisplayName("트랜잭션 AOP 적용 여부는 TransactionSynchronizationManager.isActualTransactionActive()로 확인할 수 있다")
void txTest() {
	basicService.tx();
}

현재 스레드에 트랜잭션이 적용되어 있는지 확인하는 기능이므로, 아래와 같이 테스트 코드 내에서 확인하는 방법도 생각해볼 수도 있겠다. 하지만 트랜잭션은 기본적으로 테스트에 전파되지 않으므로, 원하는 결과값인 true가 아니라 false로 출력된다.

@Test
@DisplayName("트랜잭션 AOP 적용 여부는 TransactionSynchronizationManager.isActualTransactionActive()로 확인할 수 있다")
void txTest() {
	basicService.tx();
	boolean txActive = TransactionSynchronizationManager.isActualTransactionActive();
    log.info("tx active = {}", txActive);
}

스프링 컨테이너에 트랜잭션 프록시 등록 과정

@Transactional애노테이션이 클래스 또는 메서드 레벨에 기재되어 있는 경우, 스프링은 해당 클래스를 프록시로 관리하여 스프링 빈으로 등록한다. 이 과정에 대한 전반적 흐름에 대해 살펴본다.

  1. @Transactional 애노테이션이 클래스나 메서드에 하나라도 있으면, 해당 클래스에 대한 가짜 객체를 만들어서 스프링 컨테이너에 등록한다.

  2. 만약 클래스가 xxxService라면, xxxService$$CGLIB인스턴스(이하 프록시 객체)를 생성해서 스프링 빈에 등록하는 것이다.

  3. @Autowired에 의한 의존 관계 주입도 프록시 객체가 주입되는 것이다.

  4. 프록시 객체는 xxxService를 상속하고 있다. 즉, 상속받은 기능을 그대로 사용할 수 있으므로 아래와 같은 구조가 가능해진다.

    // pseudo-code
    public void xxxService$$CGLIB extends xxxService {
      
      @Transactional
      public void bizMethod1() {
      	tx.start();
        bizMethod1();	// xxxService의 실제 메서드 호출
        tx.end();
      }
      
      public void bizMethod2() {
        bizMethod2(); // xxxService의 실제 메서드 호출
      }
    }

    @Transactional이 메서드 레벨에 적용되어 있는 경우, 애노테이션 유무에 따라 트랜잭션을 적용한다.


트랜잭션 적용 위치와 우선순위

@Transactional적용 위치에 따른 우선순위를 확인한다.

@TransactionalreadOnly옵션 이 있다. 클래스 레벨에 적용된 애노테이션은 true, 메서드 레벨에 적용된 애노테이션이 false라면 후자가 우선순위를 가진다. 톰캣, 스프링 등을 살펴보면 일반적으로 더 구체적이고 자세한 것이 우선순위를 가진다. @Transactional여부를 메서드 레벨에서 먼저 확인하여 적용하고, 없다면 클래스 레벨을 확인하여 적용한다.

@Slf4j
@Transactional(readOnly = true)
static class LevelService {

	@Transactional(readOnly = false)
	public void write() {
		log.info("call write");
		printTxInfo();
	}

	public void read() {
		log.info("call read");
		printTxInfo();
	}

	private void printTxInfo() {
		boolean txActive = TransactionSynchronizationManager.isActualTransactionActive();
		log.info("txActive: {}", txActive);
		boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
		log.info("readOnly: {}", readOnly);
	}
}
@Test
@DisplayName("트랜잭션 readOnly 옵션은 TransactionSynchronizationManager.isCurrentTransactionReadOnly()로 확인할 수 있다")
void orderTest() {
    service.write();	// readOnly: false
    service.read();		// readOnly: true
}

Reference

스프링 DB 2편 - 데이터 접근 활용 기술 - Sesction 9

👼 개인 활동을 기록합시다.

개인 활동 페이지

🧑‍🧑‍🧒‍🧒 그룹 활동을 기록합시다.

그룹 활동 페이지

🎤 미니 세미나

미니 세미나

🤔 기술 블로그 활동

기술 블로그 활동

📚 도서를 추천해주세요

추천 도서 목록

🎸 기타

기타 유용한 학습 링크

Clone this wiki locally