Spring ‐ 스프링 트랜잭션 이해 - dnwls16071/Backend_Study_TIL GitHub Wiki
📚 스프링 트랜잭션 사용 방식
- Spring - 스프링 트랜잭션
- 트랜잭션 매니저를 사용하는 방법은 크게 2가지가 있다.
- 선언적 트랜잭션 관리
@Transactional
어노테이션이 있으면 AOP에 의해 트랜잭션 처리 로직과 실제 서비스 실행 로직이 명확히 분리된다.- 프록시 덕분에 트랜잭션 처리 로직과 순수한 비즈니스 로직을 분리할 수 있다.
- 프로그래밍 방식 트랜잭션 관리
- 트랜잭션 매니저 또는 트랜잭션 템플릿 등을 사용해서 트랜잭션 관련 코드를 직접 작성하는 방식이다.
- 선언적 트랜잭션 관리
@Transactional
어노테이션이 특정 클래스나 메서드에 하나라도 있으면 트랜잭션 AOP는 프록시를 만들어 스프링 컨테이너에 등록한다.- 실제 객체 대신에 프록시인 CGLIB를 스프링 빈에 등록한다.
📚 트랜잭션 적용 위치
- 스프링에서 우선순위는 항상 더 구체적이고 자세한 것이 높은 우선순위를 가진다.
- 클래스에 적용하면 메서드는 자동 적용이 된다.
- 인터페이스에도
@Transactional
어노테이션이 적용할 수 있다. 하지만 우선순위를 잘 고려해보면 아래와 같다.- 클래스 메서드(우선순위가 가장 높다)
- 클래스 타입
- 인터페이스 메서드
- 인터페이스 타입(우선순위가 가장 낮다)
❗인터페이스에
@Transactional
어노테이션을 사용하는 것은 권장하지 않는다. AOP를 적용하는 방식에 따라서 인터페이스에 어노테이션을 두면 AOP가 적용되지 않는 경우가 있기 때문이다. 가급적 구체 클래스에@Transactional
어노테이션을 사용하자.
📚 트랜잭션 AOP 주의 사항
@Transactional
어노테이션을 사용하면 트랜잭션 처리 로직과 비즈니스 로직을 명확히 분리한다고 강조했다.- 실제로 이
@Transactional
어노테이션 트랜잭션 처리 방식은 스프링 AOP를 이용한 방법의 선언적 트랜잭션 관리 방식이고 프록시 측에서 실제 서비스를 호출한다. - 만약 프록시를 거치지 않고 대상 객체를 직접 호출하게 되면 AOP가 적용되지 않고 트랜잭션도 적용되지 않는다.
- AOP를 적용하게 되면 스프링은 대상 객체 대신에 프록시를 스프링 빈으로 등록한다.
@Slf4j
@SpringBootTest
public class InternalCallV1Test {
@Autowired CallService callService;
@Test
void printProxy() {
log.info("callService class={}", callService.getClass());
}
@Test
void internalCall() {
callService.internal();
}
@Test
void externalCall() {
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);
}
}
}
❗클라이언트 측에서 AOP Proxy callService의 external() 메서드를 호출하면 @Transactional 어노테이션이 적용되지 않았기 때문에 실제 객체 인스턴스의 external()을 호출하게 되고 내부에서 internal()를 호출하게 된다. ❗메서드 앞에 별도의 참조가 없으면 this라는 뜻으로 자기 자신의 인스턴스를 가리킨다. 결과적으로 자기 자신의 내부 메서드를 호출하기 때문에 AOP Proxy를 통한 메서드 호출이 이루어지지 않게 된다.
- 스프링의 트랜잭션 AOP 기능은 public 메서드에만 트랜잭션을 적용하도록 기본 설정이 되어 있다.
📚 예외와 트랜잭션 커밋, 롤백
- 스프링은 체크 예외는 커밋을 하고 언체크 예외는 롤백을 한다.
- 비즈니스 상황에 따라 체크 예외의 경우에도 트랜잭션을 커밋하지 않고 롤백하고 싶을 수도 있다. 이 때는 rollbackFor 옵션을 사용한다.
- 런타임 예외는 항상 롤백된다.