이번 포스트를 보기 앞서 이전 포스트를 보고 오시기 바랍니다.
https://jaystorage.tistory.com/25
@Transactional 이란?
@Transactional 어노테이션은 Spring에서 제공하는 어노테이션으로 메소드나 클래스에 적용하여 트랜잭션 경계를 지정할 수 있게 만든다. @Transactional 어노테이션을 사용하면 해당 메소드나 클래스의 메소드 실행이 트랜잭션 경계 내에서 이루어진다.
Transaction : 데이터베이스의 상태를 변화시키기 위해 수행하는 작업의 논리적 단위
@Transactional 은 Proxy 방식으로 동작한다
@Transactional 어노테이션은 Spring AOP의 대표적인 예시이다. 따라서 기존 코드를 바꾸지 않고도 AOP proxy가 기존 코드를 가로채서 앞 뒤로 transaction 처리를 할 수 있는 코드를 배치해서 하나의 transaction 으로 처리될 수 있게 만든다.
@Service
@RequiredArgsConstructor
public class SampleService {
public void insertData(){
// do something
}
}
위와 같은 코드가 있다고 가정하자. 만약 insertData 메소드에 @Transactional 어노테이션을 붙이면 다음과 같은 효과가 나타난다.
public class TransactionProxy{
private final TransactonManager manager = TransactionManager.getInstance();
public void transactionLogic() {
try {
manager.begin(); // 트랜잭션 전처리(트랜잭션 시작, autoCommit(false) 등)
insertData(); // 기존 로직
manager.commit(); // 트랜잭션 후처리(트랜잭션 커밋 등)
} catch ( Exception e ) {
manager.rollback(); // 트랜잭션 오류 발생 시 롤백
}
}
}
@Transactional은 어떤 상황에서 rollback을 진행하는가
@Transactional 어노테이션은 여러 옵션을 가지고 있다. 하지만 @Transactional에 아무 옵션을 적용하지 않으면 @Transactional은 기본적으로 RuntimeException과 Error가 발생했을 때 rollback을 진행한다.
즉 @Transactional은 아무 옵션을 적용하지 않으면 아래와 같다고 볼 수 있다.
@Transactional(rollbackFor = {RuntimeException.class, Error.class})
public void myMethod(){
…
}
@Transactional 옵션
@Transactional 은 트랜잭션의 경계를 설정할 때 아래와 같이 다양한 옵션을 설정할 수 있다.
참고로 옵션 사용 방법은 다음과 같다.
@Transactional(propagation = Propagation.REQUIRED)
public void requiredMethod() {
// 로직
}
1. 트랜잭션 전파(Propagation)
Propagation 옵션은 트랜잭션 전파 동작 방식을 정의하는데 사용된다. 전파 옵션은 메서드가 호출될 때 이미 진행 중인 트랜잭션이 있는 경우 새로운 트랜잭션을 시작할지, 기존 트랜잭션에 참여할지 등을 결정한다. 즉 Propagation은 트랜잭션을 수행하다가 다른 트랜잭션을 마주했을 때 처리 방식에 대한 옵션이다.
- REQUIRED
Default 설정. 이미 시작된 트랜잭션이 있으면 참여하고 없으면 새로 트랜잭션을 생성해 시작한다. 하나의 트랜잭션이 시작된 후 다른 트랜잭션 경계가 설정된 메소드를 호출하면 자연스럽게 같은 트랜잭션으로 묶임 - SUPPORTS
이미 시작된 트랜잭션이 있으면 참여하고, 없으면 트랜잭션 없이 진행 - MANDATORY
이미 시작된 트랜잭션이 있으면 참여하고, 없으면 예외 발생
즉 현재 트랜잭션이 반드시 존재해야 함 - REQUIRES_NEW
항상 새로운 트랜잭션을 시작하고, 이미 진행 중인 트랜잭션이 있으면 이를 일시 중지 - NOT_SUPPORTED
트랜잭션을 전혀 사용하지 않고, 이미 진행 중인 트랜잭션이 있으면 일시 중지 - NEVER
트랜잭션을 강제로 사용하지 않고, 이미 진행 중인 트랜잭션이 있으면 예외를 발생
즉 트랜잭션을 지원하지 않는 설정 - NESTED
이미 진행 중인 트랜잭션이 있으면 중첩 트랜잭션을 시작, 단 중첩 트랜잭션은 부모 트랜잭션에 영향을 주지 않음
2. 트랜잭션 격리 수준(Isolation)
Isolation 옵션은 트랜잭션 격리 수준을 설정하는 데 사용된다. 트랜잭션 격리 수준은 동시에 실행되는 여러 트랜잭션 간의 상호 작용 방식을 정의하여 데이터의 일관성과 무결성을 보장한다. 다시 말해 Isolation 옵션은 트랜잭션의 작업 결과를 다른 트랜잭션에게 어떻게 노출할 것인지를 결정하는 기준이라고 할 수 있다.
- DEFAULT
데이터 액세스 기술 또는 DB의 디폴트 설정을 따른다. 참고로 대부분의 DB는 READ_COMMITTED를 기본 격리 수준으로 갖는다. - READ_UNCOMMITTED
커밋되지 않는 데이터에 대한 읽기 허용. - READ_COMMITTED
커밋된 데이터에 대한 읽기 허용 - REPEATABLE_READ
동일 필드에 대해 다중 접근 시 모두 동일한 결과를 보장 - SERIALIZABLE
읽기 작업에도 공유 잠금을 설정하게 되고, 다른 트랜잭션에서 변경하지 못함
3. Timeout : 트랜잭션의 타임아웃 시간을 설정
4. ReadOnly : 트랜잭션을 읽기 전용으로 설정
5. rollbackFor : 롤백할 예외 유형을 지정
6. noRollbackFor: 롤백하지 않을 예외 유형을 지정
@Transactional 사용 시 주의할 점
1. 메소드가 private 으로 되어있으면 @Transactional 어노테이션을 사용할 수 없다.
프록시 객체는 타겟 객체/인터페이스를 상속받아서 구현하는데, private으로 되어 있으면 자식인 프록시 객체에서 호출할 수 없다. 따라서 @Transactional 이 붙는 메소드, 클래스는 프록시 객체에서 접근 가능한 레벨로 지정해야 한다.
2. 트랜잭션은 객체 외부에서 처음 진입하는 메소드를 기준으로 동작한다.
@Transactional 어노테이션이 붙은 메소드 안에 어떤 메소드를 호출하는데 그 메소드에도 @Transactional 어노테이션이 붙어있을 수 있다. 하지만 AOP proxy는 호출시점에 target을 가로채기 때문에 처음 진입하는 메소드를 기준으로만 동작한다.
출처 : https://www.youtube.com/watch?v=taAp_u83MwA https://www.youtube.com/watch?v=cc4M-GS9DoY https://www.youtube.com/watch?v=aX9c7z9l_u8 https://jhyonhyon.tistory.com/67 https://gngsn.tistory.com/152 |
'Spring' 카테고리의 다른 글
Bean 스코프 (0) | 2024.07.16 |
---|---|
Spring, Spring Boot 차이 (0) | 2024.07.14 |
JDK Dynamic Proxy vs CGLIB Proxy (0) | 2024.07.02 |
AOP와 Spring AOP (0) | 2024.06.26 |
SL4J와 Logback을 이용한 Logging (0) | 2024.03.03 |