기본적으로 트랜잭션이라 하면 더 이상 쪼갤 수 없는 최소의 작업 단위라는 개념이 존재한다.
따라서 트랜잭션 경계 안에서 진행된 작업은 commit()을 통해 모두 성공하던지 혹은 rollback()을 통해 모두 취소되어야 한다.
하지만 이밖에도 좀 더 세밀하게 트랜잭션의 동작 방식을 제어할 수 있는 몇 가지 조건이 존재한다.
트랜잭션 전파
transaction propagation란 트랜잭션의 경계에서 이미 진행 중인 트랜잭션이 있을 때 혹은 없을 때 어떻게 동작할 것인가를 결정하는 방식을 말한다.
예를 들어 다음과 같은 코드가 있다.
A의 트랜잭션이 진행 중이면 B의 코드는 어떤 트랜잭션 안에서 동작해야 할까?
1) A에서 트랜잭션이 실행 중이므로 B의 코드는 새로운 트랜잭션을 만들지 않고 참여하여 성공과 실패를 공유한다.
2) A와 별도로 트랜잭션을 가져와 실행한다. 즉 독립적인 트랜잭션이 된다.
이렇게 B와 같이 독립적인 트랜잭션 경계를 가진 코드에 대해 이미 진행 중인 트랜잭션이 어떻게 영향을 줄지 정의하는 것이 트랜잭션 전파 속성이다.
여러 전파 속성이 존재한다.
1. PROPAGATION_REQUIRED : 진행 중인 트랜잭션이 없으면 새로 시작하고, 존재한다면 해당 트랜잭션에 참여한다.
2. PROPAGATION_REQUIRED_NEW : 항상 새로운 트랜잭션을 시작한다. 이 속성은 독립적인 트랜잭션이 보장돼야 하는 코드에 적용할 수 있다.
3.PROPAGATION_NOT_SUPPORTED : 트랜잭션 없이 동작하도록 만든다. 대구 스프링에서 트랜잭션 경계설정은 보통 AOP를 이용해 한 번에 많은 메서드에 동시에 적용하는 방법을 사용한다. 그중에서 만약 특별한 메서드만 트랜잭션 적용에서 제외하려면 어떻게 할까? 포인트 컷을 만들어서 제외하는 방법 있겠지만 포인트 컷이 복잡해질 수 있다.
음. 만약 커피가게에서 커피를 주문하는데 기본적으로 커피와 빨대 냅킨이 나간다.
커피와 빨대는 하나의 트랜잭션으로 꼭 처리된다고 하고 냅킨은 나가도 되고 안 나가도 된다고 생각했을 때를 상상해본다.
그럼 냅킨에는 트랜잭션 전파속성은 NOT_SUPPORTED로 적용하면 냅킨이 나가지 않아서 이미 나온 커피와 빨대를 되돌리는 작업을 하지 않기 때문에 사용한다고 할 수 있다.
스프링 지원 전파 속성
- REQUIRED : 트랜잭션 필요. 진행 중이라면 해당 트랜잭션 사용. 없으면 생성
- MANDATORY : 트랜잭션 필요. 트랜잭션 존재하지 않으면 익셉션 발생
- NEVER : 트랜잭션이 불필요. 진행 중인 트랜잭션 존재하면 익셉션 발생
- NESTED : 기존 트랜잭션 존재하면 중첩된 트랜잭션에서 메서드 실행
격리 수준
모든 DB 트랜잭션은 격리수준을 갖고 있어야 한다. 서버 환경에서는 다양한 트랜잭션이 동시에 진행된다.
만약 이러한 트랜잭션이 순차적으로 실행되어 다른 트랜잭션의 작업에 영향을 안 미치면 좋겠지만 이러면 성능이 크게 떨어질 것이다. 따라서 적절하게 격리 수준을 조정해서 가능한 많은 트랜잭션을 동시에 진행시키면서 문제가 발생하지 않게 하는 제어가 필요하다.
격리 수준은 DB에 기본적으로 설정되지만 JDBC 드라이버나 DataSource 등에서 재설정할 수 있고, 필요하면 트랜잭션 단위로 조절할 수도 있다.
스프링 지원 격리 수준
- DEFAULT : 기본 설정
- READ_UNCOMMITED : 다른 트랜잭션에서 커밋하지 않는 데이터 읽기 가능
- READ_committed : 다른 트랜잭션에 의해 커밋된 데이터 읽기 가능
- REPEATABLE_READ : 처음 읽은 데이터와 두 번째 읽은 데이터가 동일
- SERIALIZABLE : 동일한 데이터에 대해 두 개 이상의 트랜잭션 수행 불가
제한시간
트랜잭션을 수행하는 제한시간을 설정할 수 있다.
읽기 전용
읽기 전용으로 설정해두면 트랜잭션 내에서 데이터를 조작하는 시도를 막아 줄 수 있다. 또한 데이터 액세스 기술에 따라서 성능이 향상될 수도 있다.
스프링은 TranactionInterceptor라는 편리하게 트랜잭션 경계설정 어드바이스로 사용할 수 있도록 만들어진 것이 존재한다. 이 어드바이스는 트랜잭션의 정의 (격리, 읽기, 제한시간, 전파)를 메서드 이름 패턴을 이용해서 다르게 지정할 수 있는 방법을 추가로 제공해줄 뿐이다.
PlatformTransactionManager와 Properties 타입의 두 가지 프로퍼티를 가지고 있다.
PlatformTransactionManager는 인터페이스로 각 DB 연동 기술에 따른 트랜잭션 매니저를 제공해준다.
추상화시켰기 때문에 각 DB연동 기술에 따라 DatasourceTrascationManager와 JPA, JTA 등등 다양한 구현체를 DI 받아 사용한다.
Properties은 트랜잭션 속성을 정의했다.
Properties 타입의 transactionAttributes 프로퍼티는 메서드 패턴에 따라서 각기 다른 트랜잭션 속성을 부여할 수 있게 하기 위해서 맵 타입 컬랙션을 사용한다.
다음과 같은 속성을 문자열로 정의할 수 있다.
트랜잭션 전파 방식, 격리 수준, 읽기전용, 제한시간, 체크 예외 중 롤백 대상으로 추가할 예외, 언체크 예외 중 롤백시키지 않을 예외
를 정의할 수 있다. 이중에서 트랜잭션 전파 방식만 필수이고 나머지는 다 생략 가능하다.
대부분 디폴트로 사용한다.
@Bean
public TransactionInterceptor transactionAdvice(){
TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
Properties properties = new Properties();
properties.put("get*", "PROPAGATION_REQUIRED,readOnly,timeout_30");
properties.put("upgrade*", "PROPAGATION_REQUIRES_NEW,ISOLATION_SERIALIZABLE");
properties.put("*", "PROPAGATION_REQUIRED");
transactionInterceptor.setTransactionManager(transactionManager());
transactionInterceptor.setTransactionAttributes(properties);
return transactionInterceptor;
}
여기서 주의사항이 하나 있다. 메서드 실행을 위임받은 실제 타깃 오브젝트 내에서 해당 오브젝트의 메서드를 여러 개 실행하면 어드바이스가 적용되지 않는다.
트랜잭션 속성을 새로 시작하도록 설정해도 결국 첫 번째 실행된 트랜잭션 속성을 따르게 되어있기 때문에 트랜잭션 속성이 달라도 의미가 없다.
public class UserService{
UserDao userdao
public User get(String id){ userdao.get(id);}
}
트랜잭션 경계 설정을 정할 때 특정 계층의 경계를 트랜잭션 경계와 일치시키는 것이 바람직하다.
비즈니스 로직을 담고 있는 서비스 계층 오브젝트의 메서드가 트랜잭션 경계를 부여하기에 가장 적절한 대상이다.
이 UserService 클래스는 userDao에게 해당 요청을 위임하도록 되어 있다.
이것은 모든 User관련 데이터 조작은 UserService라는 트랜잭션 경계를 통해 진행할 경우 모두 트랜잭션을 적용할 수 있게 통일되었다.
여기서는 다른 계층이나 모듈에서 DAO에 직접 접근하는 것은 차단해야 한다. 트랜잭션은 보통 서비스 계층의 메서드 조합을 통해 만들어지기 때문에 DAO가 제공하는 주요 기능은 서비스 계층에 위임 메서드를 만들어 둘 필요가 있다. 다른 모듈이 DAO에 접근할 때는 서비스 계층을 거치도록 하여 트랜잭션 설정은 단순화할 수 있다.
정리
스프링이 제공하는 빈 후처리기 인 자동 프락시 생성기를 사용하고 이 자동 프락시 생성기는 advisor를 통해
부가기능과 해당 위치를 정하고 타깃이 빈으로 생성될 때 프록시를 생성한다.
트랜잭션 어드바이저를 통해 프록시 빈이 생성됐고 이 트랜잭션 어드바이저에서 트랜잭션을 가져올 때 트랜잭션의 속성을 정의했다.
이제 클라이언트가 타깃 오브젝트를 사용할 때 tx부가기능이 추가되어 타깃 오브젝트의 메서드가 실행된다.
'Spring|Spring-boot' 카테고리의 다른 글
Validation (0) | 2021.06.10 |
---|---|
Spring Transaction(2) (0) | 2021.04.03 |
Spring Security(3) (0) | 2021.03.28 |
[Spring ] 빈 객체의 초기화와 소멸 (0) | 2021.01.25 |
[Spring] 의존 자동 주입 대상의 필수 유무설정 (0) | 2021.01.25 |
댓글