IT 개발

[Spring] 트랜잭션 AOP 기능 정리

썬프로 2023. 1. 7. 19:00
반응형

2023.01.06, 금융상품권 관련 신규 서비스를 개발하던 중, exception 발생 시 Transaction Rollback이 안 되는 현상이 발생했다. 관련해서 Transaction 처리 서비스를 뜯어서 정리해보고 어떤 점이 문제였는지 기록해두려 한다.

모바일 앱에서 금융상품권을 등록하면, 직판 코어 시스템으로 요청을 보내고 거기서 쿠프마케팅이라는 금융상품권 시스템으로 상품권등록 api호출을 진행하였다. 쿠프마케팅 측에서 정상 리턴을 제공받고, 향후 코어 시스템 서비스 로직 수행 중 에러가 발생할 경우, 예외처리를 통해 쿠프마케팅 시스템에 망취소 api를 호출해 줘야 했다.

테스트 진행을 위해 강제 익셉션을 발생 시켰고, 쿠프마케팅 쪽에 정상적으로 망취소 서비스까지 호출하여 리턴을 받았으나, 기존 try문 안에서 실행되던 등록과정의 로직들이 롤백이 되지 않는 현상을 보게 되었고, 문제점을 찾게 되었다.

Springboot 프레임워크로 개발된 시스템은 Transaction처리를 위해 aop를 이용하고 있었다.

사실 일반적으로 많이 사용하는 방식은 아니어서 조금더 혼란이 있었고, 금융시스템 특성상 예전부터 개발되어이 있던 부분들을 살려서 진행하다 보니 아래와 같은 형태로 DBConfig를 만들어서 트랜잭션 처리를 하고 있었다.

@Bean(name= "transactionInterceptor")
public TransactionInterceptor transactionInterceptor() {
    TransactionInterceptor transactionInterceptor = new TransactionInterceptor();

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

    DefaultTransactionAttribute readOnlyAttribute = new DefaultTransactionAttribute(
            TransactionDefinition.PROPAGATION_SUPPORTS);
    readOnlyAttribute.setReadOnly(true);
    readOnlyAttribute.setTimeout(TX_METHOD_TIMEOUT);

    RuleBasedTransactionAttribute writeAttribute = new RuleBasedTransactionAttribute(
            TransactionDefinition.PROPAGATION_REQUIRED, rollbackRules);
    writeAttribute.setTimeout(TX_METHOD_TIMEOUT);

    RuleBasedTransactionAttribute newWriteAttribute = new RuleBasedTransactionAttribute(
            TransactionDefinition.PROPAGATION_REQUIRES_NEW, rollbackRules);
    newWriteAttribute.setTimeout(TX_METHOD_TIMEOUT);

    Properties txAttributes = new Properties();

    // PROPAGATION_REQUIRED
    txAttributes.setProperty("insert*", writeAttribute.toString());
    txAttributes.setProperty("update*", writeAttribute.toString());
    txAttributes.setProperty("delete*", writeAttribute.toString());
    txAttributes.setProperty("ins*", writeAttribute.toString());
    txAttributes.setProperty("upd*", writeAttribute.toString());
    txAttributes.setProperty("del*", writeAttribute.toString());
    txAttributes.setProperty("process*", writeAttribute.toString());
    txAttributes.setProperty("proc*", writeAttribute.toString());
    txAttributes.setProperty("save*", writeAttribute.toString());
    txAttributes.setProperty("exec*", writeAttribute.toString());
    txAttributes.setProperty("add*", writeAttribute.toString());
    txAttributes.setProperty("perform*", writeAttribute.toString());
    txAttributes.setProperty("execute*", writeAttribute.toString());

    // PROPAGATION_REQUIRES_NEW
    txAttributes.setProperty("partial*", newWriteAttribute.toString());
    txAttributes.setProperty("divideTr*", newWriteAttribute.toString());
   
    // READONLY
    txAttributes.setProperty("*", readOnlyAttribute.toString());
    transactionInterceptor.setTransactionAttributes(txAttributes);
    transactionInterceptor.setTransactionManager(transactionManager());

    return transactionInterceptor;
}

@Bean(name="transactionAdvisor")
public AspectJExpressionPointcutAdvisor transactionAdvisor() {
    AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor();
    advisor.setAdvice(transactionInterceptor());
    advisor.setExpression("(execution(* com.daumit.core..service.*Service.*(..))"
                     + " || execution(* com.daumit.fts..service.*Service.*(..)) "
                     + " || execution(* com.daumit.fts.ba.batch.job.*Job.*(..)))");
    return advisor;
}

transactionAdvisor에서 해당경로의 메서드 실행시 트랜잭션을 구성하도록 advisor를 구성하였고, 관련해서 advisor의 트랙잭션 속성을 transactionInterceptor를 통해 세팅하였다.

transactionInterceptor Bean을 보면, PROPAGATION_SUPPORTS, PROPAGATION_REQUIRED, PROPAGATION_REQUIRES_NEW 3가지로 세팅이 되어있었다.

각각 "읽기전용모드(트랙잭션처리 상관 안 함)", "기존 트랜잭션을 이어서 처리", "새로운 트랜잭션을 생성하여 처리"를 의미한다.

나의 경우에는 메서스명을 잘못 설정해서, 새로운 트랜잭션을 생성하여 처리하다 보니 내가 만든 메서드를 통해 실행된 쿼리들이 롤백이 안 되는 현상이었다.

반응형