IT/development

[spring] AOP로 트랜잭션 관리 (feat. 롤백 처리)

어흥꼬비 2024. 12. 1.
반응형

시니어 개발자가 초기 설정 한 프로젝트 소스를 체크아웃 받아 API 개발 중 트랜잭션 롤백 관련 삽질한 경험이다.

코드를 보니 아래와 같이 공통에서 AOP를 통해 트랜잭션을 자동으로 관리하도록 설정이 되어 있었다.

원인은 정말 허무했지만 나중의 삽질을 미연에 방지도록 기록한다.(원래 뭐든 알고나면 쉽지 않은가 😎)

TransactionConfig 

package kr.test.portal.config.spring;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionInterceptor;

import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

@Slf4j
@Aspect
@Configuration
@RequiredArgsConstructor
public class TransactionConfig {

    private final PlatformTransactionManager txManager;

    @Bean
    public TransactionInterceptor txAdvice() {

        TransactionInterceptor txAdvice = new TransactionInterceptor();

        List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();
        rollbackRules.add(new RollbackRuleAttribute(Exception.class));

        DefaultTransactionAttribute attribute = new RuleBasedTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED, rollbackRules);
        String transactionAttributesDefinition = attribute.toString();

        Properties txAttributes = new Properties();
        txAttributes.setProperty("select*", transactionAttributesDefinition);
        txAttributes.setProperty("insert*", transactionAttributesDefinition);
        txAttributes.setProperty("update*", transactionAttributesDefinition);
        txAttributes.setProperty("delete*", transactionAttributesDefinition);
        txAttributes.setProperty("create*", transactionAttributesDefinition);
        txAttributes.setProperty("add*", transactionAttributesDefinition);
        txAttributes.setProperty("modify*", transactionAttributesDefinition);
        txAttributes.setProperty("remove*", transactionAttributesDefinition);
        txAttributes.setProperty("useTransaction*", transactionAttributesDefinition);
        txAttributes.setProperty("select*", transactionAttributesDefinition);

        txAdvice.setTransactionAttributes(txAttributes);
        txAdvice.setTransactionManager(txManager);

        return txAdvice;

    }

    @Bean
    public DefaultPointcutAdvisor txAdviceAdvisor() {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution(* kr.test.portal..impl.*Impl.*(..))");
        return new DefaultPointcutAdvisor(pointcut, txAdvice());
    }
}

1. 트랜잭션 인터셉터를 설정하여 롤백 규칙과 트랜잭션을 적용할 메서드명에 대한 규칙을 정의

2. 트랜잭션 인터셉터와 포인트컷을 연결하는 어드바이저를 생성하여 반환(포인트컷을 사용하여 트랜잭션을 적용할 대상 메서드 경로 설정)

이 두 개의 빈을 통해 AOP가 적용된 메서드가 실행될 때 자동으로 트랜잭션을 시작하며, 정상 처리 시 커밋되고, 예외 발생 시 롤백되도록 되어 있었다.

포인트컷의 경로에 Impl클래스를 만들고 비즈니스 로직을 만들며 테스트 하고 있었는데 예외가 발생해도 롤백이 되지 않았었다. 😥

원인은 바로 대상 메서드에서 발생한 예외를 밖으로 던져야 트랜잭션 인터셉터가 개입할 수 있는데 내 경우는 예외 발생 시 그냥 catch문에서 로그만 출력하고 끝냈었다.

어차피 해당 로직은 open API 호출해서 가져온 데이터를 DB에 저장만 하고 프론트 단에서 딱히 후처리하는게 없어그냥 try catch를 지우고 throws Exception으로 예외를 던진 뒤 정상적으로 롤백이 된 걸 확인 할 수 있었다.


개인 스터디 기록을 메모하는 공간이라 틀린점이 있을 수 있습니다.

틀린 점 있을 경우 댓글 부탁드립니다.

반응형

댓글

💲 추천 글