@Transactional
- 메서드나 클래스에 적용하여, 이 단위가 완전하게 실행되거나 완전히 롤백되도록 보장하는 어노테이션
- 내부적으로 프록시라는 기술을 사용
프록시
- 실제 객체에 대한 대리 객체를 생성
- 대리 객체가 호출을 가로채고 추가 작업(로깅, 트랜잭션)을 수행한 후 실제 객체에 전달하는 기술
프록시 실습
// TodoProjectApplicationTests.java
@Autowired
private TodoService todoService;
@Test
void logIsProxy(){
log.info("\nTodoService : {}", todoService.getClass());
}
-> TodoService의 구현체 TodoServiceImpl이 정상적으로 주입
👇 TodoServiceImpl에 @Transactional 어노테이션을 적용하고 test
@Transactional
public TodoResponseDto addTodo(TodoRequestDto dto) {
...
}
-> TodoServiceImple이 Spring CGLIB 프록시 객체로 감싸져 있음
CGLIB
- 클래스 기반으로 프록시 객체를 생성하는 방식
- 기존 클래스를 상속해서 프록시를 생성
@Transactional 내부 동작 원리
- 해당 메서드가 호출되면 프록시 객체 생성
- 프록시 객체가 TransactionInterseptor 실행
- TransactionInterceptor가 트랜잭션 설정
- @Transactional에 지정한 속성에 따라서 적절한 트랜잭션 매니저 선택
- 트랜잭션 매니저가 커넥션 생성
- 커넥션 내부의 setAutoCommit(false) 실행 -> JDBC 연결의 자동 커밋을 비활성화
- 트랜잭션 동기화 매니저에 커넥션 저장 -> 동일한 커넥션을 하나의 스레드에서 일관되게 사용하도록 보장
- 프록시 객체가 실제 타겟 메서드 호출
- TransactionInterceptor가 해당 트랜잭션 작업이 끝나면 commit / 예외가 발생하면 rollback
@Transactional 사용 시 주의사항
1. 하나의 클래스에 A메서드와 @transactional이 적용된 B메서드가 있다고 가정할 때, A메서드가 B메서드를 호출하면 트랜잭션 적용 안 됨
원인 : 같은 클래스 내에서의 메서드 간 호출은 프록시를 거치지 않음
해결 방법 : 두 메서드를 다른 클래스로 분리
2. private 메서드에 적용 불가
-> CGLIB는 타겟의 클래스를 상속받아 프록시 생성하므로, private 메서드를 상속받을 수 없어 적용이 불가능하다
@Transactional 테스트
- 롤백 테스트
- @Transactional이 붙은 테스트는 종료 후 트랜잭션이 자동으로 롤백되어, 테스트 중 발생한 데이터 변경 사항이 데이터베이스에 반영되지 않는다.
- 테스트 간 데이터 충돌을 방지할 수 있다.
JPA 변경 감지
엔티티 객체의 상태 변화를 추적해서 변경된 데이터를 DB에 자동으로 반영하는 기능
@Transactional이 있을 때
JPA 변경 감지를 통해 DB에 데이터가 잘 변경되는 것을 확인할 수 있으므로, 테스트 성공 예측이 가능하다.
테스트가 끝난 후 롤백해서 테스트 결과를 DB에 반영하지 않음
@Transactional이 없을 때 (기본 테스트 방식)
변경 감지를 안하기 때문에 DB에 데이터가 잘 변경되는지 확인할 수 없다.
만약 DB에 데이터가 잘 변경되는지 확인하려면 flush()를 실행해야한다.
flush()를 통해서 DB에 테스트 결과를 반영했다면, 테스트가 끝난 후에도 롤백되지 않는다.
👇
@Transactional 테스트 진행 시 주의사항
실제 서비스 코드에 @Transactional을 사용하지 않는 상황에서, 해당 메서드가 제대로 실행되지 않더라도
@Transactional을 적용한 테스트 코드는 JPA가 변경 감지를 하기 때문에 의도대로 작업이 진행된다고 예상할 수 있다.
'언어, 프레임워크 > Spring' 카테고리의 다른 글
JPA 알아보기 (+Spring Data JPA 적용) (0) | 2025.03.31 |
---|---|
SOLID 원칙과 Bean 등록 방법 (0) | 2025.03.28 |
[부트캠프 5주차] 새로 알게된 개념, 메서드 정리 (0) | 2025.03.23 |
Spring Boot의 예외처리 (유효성 검사) (0) | 2025.03.20 |
응답(Response) 데이터 전달 방식 (0) | 2025.03.20 |