@Transactional
어노테이션이나 AOP로 트랜잭션을 처리하는 방법으로 데이터의 일관성을 유지하면서 안정적으로 데이터를 복구하기 위해 사용하는 형태입니다.
이러한 방법은 위에서 언급한 어노테이션으로 설정하는 방법과
AOP로 지정한 메서드 이름에 Transactional을 부여하는 방법 2가지가 존재합니다.
나는 2가지의 방법을 시도하였으나 트랜잭션에 작성에는 문제가없었지만 롤백이 되지 않는 문제가 발생하였습니다.
처음에는 트랜잭션의 설정을 잘못한 줄 알아 계속해서 설정하는 방법을 찾았으나 설정 자체에는 문제가 없다는 것을 깨닫게 되었고 다른 방법을 찾게 되었습니다.
안되는 이유에 대해서 한참을 찾게 되었는데, 검색으로 나온 결과론
첫 번째 가설은 Checked Exception의 문제였습니다,
@Transactional 어노테이션은 기본적으로 Checked Exception에 대해서는 롤백시키지 않도록 되어있기 때문에 해당 문제를 파악해봐야했습니다.
Checked Exception?
해당 오류는 개발자들이 만든 애플리케이션 코드에서 예외가 발생했을 경우 사용하게 되는데 이때 Unchecked Exception은 RuntimeException을 상속하고 Checked Exception은 RuntimeException을 상속하지 않는다는 차이가 있습니다.
그래서 RuntimeException을 상속하지 않으므로 반드시 예외처리를 해줘야 트랜잭션에서 롤백이 가능해지는 구조가 되는 것입니다. 이때 Checked Exception에는 SQLException도 포함이 되어있기 때문에 임시적으로 트랜잭션을 파악하기 위해 insert문에 null값을 넣어 에러를 뜨게 했었으므로 그것에 대한 검증이 필요했습니다.
그래서 해당 오류가 일어나는 것인지 확인하기위해 메소드 중간에 throw RuntimeException을 하기도 하였으며 아니면 트랜잭션 내에서 @Transactional(rollbackFor = {Exception.class})으로 설정도 시도해보았지만 그럼에도 실행이 되지않는 모습을 확인할 수 있었습니다.
사실 애초에 AOP를 통해 설정한 값에 의하면 이미 rollback-for에 대한 exception처리를 Exception으로 처리하였기 때문에 checked Exception이 아니라는 것을 확인할 수 있었습니다.
<!-- 공통 -->
<!-- Transaction 설정하기 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- AOP 트랜잭션 설정 -->
<!-- AOP Pointcut/Advisor 설정 -->
<aop:config>
<aop:pointcut id="txMethod" expression="execution(* com.buff..*Service.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txMethod"/>
</aop:config>
<!-- AOP Transaction을 실행하는 대상 Method 설정 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="insert*" rollback-for="Exception"></tx:method>
<tx:method name="create*" rollback-for="Exception"></tx:method>
<tx:method name="update*" rollback-for="Exception"></tx:method>
<tx:method name="delete*" rollback-for="Exception"></tx:method>
<tx:method name="edit*" rollback-for="Exception"></tx:method>
<tx:method name="regist*" rollback-for="Exception"></tx:method>
<tx:method name="remove*" rollback-for="Exception"></tx:method>
<tx:method name="select*" read-only="true"></tx:method>
<tx:method name="get*" read-only="true"></tx:method>
</tx:attributes>
</tx:advice>
<!-- 어노테이션 -->
<!-- 애너테이션 기반의 트랜잭션 제어를 활성화함 -->
<!-- <tx:annotation-driven/> -->
그래서 다른 정보를 찾다가 우연히 쓱 지나치다가 보고 나중에 다른 설정파일을 보다가 발견한 내용이었습니다.
해당 글은 https://wildeveloperetrain.tistory.com/336 이 게시글입니다.
원인은 'root-context.xml' 파일과 'servlet-context.xml' 파일이 잘못된 것이었고 그중에서 문제는
'servlet-context.xml'에서의 '<context:component-scan/>'의 항목이었습니다.
원래 위의 항목을 root-context.xml에 넣었지만
tx:annotation-driven이나 tx:advice 설정이 적용될 때 해당 @Transactional 어노테이션을 사용하는 대상은 이미 빈으로 등록된 상태여야하는데
파일이 로딩 순서는 root-context.xml > servlet-context.xml 순서로 로딩이 되기 때문에 빈으로 등록되는 순서가 뒤에 적용되므로 트랜잭션이 적용되지 않았다는 사실을 알게 되었습니다.
그래서 공통 부분은 root-context.xml에 그대로 넣고 아랫부분은 servlet-context.xml에 넣어서 트랜잭션이 잘 돌아가는 것으로 확인할 수 있었습니다.
마무리
문제 해결과정에서 트랜잭션을 설정 시에 Exception같은 설정 같은 것이나 그리고 설정에 대한 동작 순서 그리고 설정 파일에 대한 로딩 순서라던지 처음 배우는 정보에 대해서 많이 배울 수 있었던 것같습니다.
해당 외에 설정 적용하는 것을 더 세세히 공부해서 나중에 기록할 수 있도록 해야겠습니다.
'Project' 카테고리의 다른 글
개인 프로젝트 - 2. 프로젝트 설정, mysql 연결 (2) | 2024.11.01 |
---|---|
개인 프로젝트 - 1. 프로젝트 생성 (0) | 2024.11.01 |
ERP 프로젝트 - 보안(수업) (0) | 2024.10.12 |
ERP 프로젝트 - FullCalendar (1) | 2024.10.11 |
ERP 프로젝트 - 최저가 표 (0) | 2024.10.11 |