토비의 스프링 - 6장 (4)
AOP 로 시작했던 6장의 마지막 내용입니다.
AOP의 예시로 트랜잭션을 구현했었는데 트랜잭션의 경계안에서 진행된 작업이 전부 commit 되거나 전부 rollback 되어야 한다. 그런데 이런 트랜잭션의 동작 방식을 제어 할 수 있는 속성이 있습니다.
이는 다음과 같습니다.
-
트랜잭션 전파
독자적인 트랜잭션 경계를 가진 코드가 이미 진행중인 트랜잭션이 어떻게 영향을 미칠 수 있는가를 정의하는 것으로 각각의 트랜잭션 경계를 가진 트랜잭션들이 겹쳤을 때 트랜잭션을 어떻게 관리 할지를 의미합니다.
- PROPAGATION_REQUIRED : 진행중인 트랜잭션이 없으면 새로 생성하고 진행중인 트랜잭션이 있다면 이에 참여함
- PROPAGATION_REQUIRES_NEW : 앞서 진행중인 트랜잭션이 있어도 새로 생성해 독자적으로 동작하게 됨
- PROPAGATION_NOT_SUPPORTED : 트랜잭션 없이 동작하도록 하는 것
- AOP 를 통한 트랜잭션 경계설정은 보통 한번에 많은 메소드를 동시에 적용하기 위해 사용하는데 특별한 메소드만 적용에서 제외할 때 사용
-
격리수준
서버 환경에서의 트랜잭션은 동시에 진행 될 수 있는데 이 때의 트랜잭션들이 서로 변경한 데이터를 볼 수 있을지 말지를 결정하는 것을 의미합니다. 일반적으로는 DB의 트랜잭션 격리수준을 따라가는 편이 좋습니다. 다만 특별한 작업을 하는 메소드라면 독자적인 격리수준을 지정할 필요가 있습니다.
크게 4개로 나뉩니다.
- READ UNCOMMITTED : 커밋 이전에도 데이터 조회가 가능합니다.
- READ COMMITTED : 커밋 이후에만 데이터 조회가 가능합니다.
- REPETABLE READ : 트랜잭션이 시작되기 전의 커밋된 내용에 대해서만 조회할 수 있습니다.
- SERIALZABLE : 읽기 작업만 하는 트랜잭션도 데이터가 잠금되어 조회할 수 없습니다.
-
제한시간
트랜잭션 수행 시의 제한시간을 설정하는걸 의미합니다.
-
읽기전용
트랜잭션 내에서의 데이터 조작 시도를 막아줄 수 있습니다.
다음은 트랜잭션 속성을 적용할 때의 이야기 입니다. 기본 원리는 이전의 메소드 이름 패턴에 따라 트랜잭션 적용 여부를 판단했던 것처럼 메소드 이름 패턴에 따라 다른 트랜잭션 정의가 적용되면 됩니다.
TransactionInterceptor 의 transactionAttributes 인터페이스를 통해 속성을 정의하고 예외처리를 통해 속성을 제어합니다. 그리고 Properties 라는 맵 형태의 오브젝트를 TransactionInterceptor 이 전달받아 메소드 패턴에 따라 각기 다른 트랜잭션 속성을 부여합니다.
마지막으로 프록시 방식의 AOP를 사용할 때 주의점이 있습니다.
클라이언트에서 프록시를 통해 메소드를 호출 할때는 부가기능이 이루어지게 되는데 문제는 오브젝트가 자기 자신의 메소드를 호출 할때는 프록시를 통한 호출이 아니기 때문에 부가기능의 적용이 일어나지 않게 됩니다.
이렇게 세밀한 트랜잭션 속성 제어가 필요할 때를 대비해 스프링에서는 직접 타겟에 어노테이션을 지정하는 방법이 있습니다.
@Transactional 을 통해서 정의하게 됩니다. 이때 적용 단위는 메소드 단위로 메소드마다 어노테이션을 부여할 수 있습니다. 다만 이런 어노테이션을 많이 사용할 수록 코드가 지저분해 질 수도 있습니다.
프록시 AOP 의 종류와 특징, 비 프로식시 방식 AOP 의 동작원리를 잘 파악하고 @Transactional 의 적용 대상을 잘 관리 해줄 수 있다면 인터페이스에 @Transactional 을 적용하고 사용해도 됩니다.
이전의 AOP를 사용해 코드 외부에서 트랜잭션 기능을 부여하는 것을 선언적 트랜잭션, 반대의 경우를 프로그램에 의한 트랜잭션이라고 합니다.
이런 프로그램에 의한 트랜잭션을 사용하기 좋은 장소는 테스트인데 이는 메소드 단위로 테스트를 진행하기에 굉장히 유용하게 사용됩니다.
유용하게 사용되는 어노테이션으로 롤백 기능을 제어하기 위한 @Rollback 과 테스트 클래스의 모든 테스트 메소드 일괄 적용을 위한 @TransactionConfiguration 이 있습니다. 그리고 일부 메소드 중 트랜잭션을 시작하지 않은 채로 테스트를 진행하는 @NotTransactional 이 있습니다.