관리 메뉴

너와 나의 스토리

[Spring] @Transaction과 AOP를 이용해서 트랜잭션 처리하기 & 차이점 본문

개발/Spring Boot

[Spring] @Transaction과 AOP를 이용해서 트랜잭션 처리하기 & 차이점

노는게제일좋아! 2021. 1. 13. 16:34
반응형

Transaction의 속성: ACID 

  • ACID
    • Atomicity(원자성): 트랜잭션은 하나 이상의 관련된 동작을 하나의 작업 단위로 처리하고, 그 결과가 성공 또는 실패할 경우 관련된 동작은 모두 동일한 결과가 나온다. 즉, 작업 중 하나라도 실패한다면 관련된 트랜잭션 내에서 먼저 처리한 동작들도 모두 처음 상태로 돌아간다.
    • Consistency(일관성): 트랜잭션이 성공적으로 처리되면 데이터베이스의 관련된 모든 데이터는 일관성을 유지해야 한다.
    • Isolation(고립성): 트랜잭션은 독립적으로 처리된다. 서로 다른 트랜잭션이 동일한 데이터에 동시에 접근할 경우 적절한 동시 접근 제어를 해야 한다.
    • Durability(지속성): 트랜잭션이 성공적으로 처리되면 그 결과는 지속적으로 유지되어야 한다.
  • 이 속성 중 트랜잭션을 가장 잘 표현한 것은 원자성이다. 
    • "작업 중 하나라도 실패한다면 관련된 트랜잭션 내에서 먼저 처리한 동작들도 모두 처음 상태로 되돌린다"
    • -> 이를 Rollback이라고 한다.

 

 

 

테스트할 상황:

  • 다음과 같이 게시판이 있고, 게시글은 조회하게 되면 조회수를 가진다.
  • 이때, 해당 게시글을 조회하는 과정에서 에러가 나는 경우를 테스트하려고 한다.
public BoardDto selectBoardDetail(int boardIdx) throws Exception{
    boardMapper.updateHitCount(boardIdx); // 조회수 올리기
    int a = 10/0;
    BoardDto board = boardMapper.selectBoardDetail(boardIdx); // 게시글 보여주기

    return board;
}
  • 위 코드에서 알 수 있듯, 조회수를 올린 후 게시글을 display하기 전에 의도적으로 에러를 발생시킨다.

 

  • 아무런 조취를 취하지 않고 위 코드로 작업을 수행한 경우, 다음과 같은 결과를 얻게 된다.

  • 이처럼 게시글 조회하는 과정에서 에러가 발생했음에도 불구하고, 다시 게시글 목록을 보면 조회수가 증가했음을 확인할 수 있다.

 

 

 

방법 1: @Transaction 이용

  • 해당 서비스 위에 @Transaction 어노테이션을 붙여주자!
@Service
@Transactional
public class BoardServiceImpl implements BoardService {

    @Autowired
    private BoardMapper boardMapper;
    
    @Override
    public BoardDto selectBoardDetail(int boardIdx) throws Exception{
        boardMapper.updateHitCount(boardIdx); // 조회수 올리기
        int a = 10/0;
        BoardDto board = boardMapper.selectBoardDetail(boardIdx); // 게시글 보여주기

        return board;
    }

}
  • 그 결과 다음과 같이, 조회수가 증가하지 않았음을 확인할 수 있다.

 

 

 

 

방법 2: AOP 이용

  • 앞에서 선언한 @Transaction을 삭제하고 아래의 코드(파일)를 추가해준다.
package com.example.board.board.aop;

import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.interceptor.MatchAlwaysTransactionAttributeSource;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionInterceptor;

import java.util.Collections;

@Configuration
public class TransactionAspect {

    private static final String AOP_TRANSACTION_METHOD_NAME="*";
    private static final String AOP_TRANSACTION_EXPRESSION="execution(* board.service.*Impl.*(..))";

    @Autowired
    private PlatformTransactionManager transactionManager;

    @Bean
    public TransactionInterceptor transactionAdvice(){
        MatchAlwaysTransactionAttributeSource source = new MatchAlwaysTransactionAttributeSource();
        RuleBasedTransactionAttribute transactionAttribute = new RuleBasedTransactionAttribute();
        transactionAttribute.setName(AOP_TRANSACTION_METHOD_NAME);
        transactionAttribute.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
        source.setTransactionAttribute(transactionAttribute);

        return new TransactionInterceptor(transactionManager, source);
    }

    @Bean
    public Advisor transactionAdviceAdvisor(){
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(AOP_TRANSACTION_EXPRESSION);
        return new DefaultPointcutAdvisor(pointcut, transactionAdvice());
    }
}
  •  결과는 @Transactional 어노테이션을 사용한 것과 같이 정상적인 결과를 얻을 수 있다.

 

 

@Transaction을 이용한 트랜잭션 vs AOP이용한 트랜잭션

  @Transaction을 이용한 트랜잭션 AOP이용한 트랜잭션
장점 - 특별한 설정 없이 쉽게 사용 가능
- 원하는 곳에서만 트랜잭션 설정함으로써 성능에 대한 영향을 최소화 할 수 있음
- 공통으로 트랜잭션이 적용되기 때문에 트랜잭션이 누락될 일이 없다
- 외부 라이브러리에도 적용 가능
단점 - 어노테이션이 누락되거나 여러 메서드에 걸쳐서 사용될 경우 트랜잭션이 적용되지 않을 수 있다.
- 외부 라이브러리에는 적용 불가
- 원하는 곳에만 트랜잭션 적용 힘듦 -> 성능에 영향 미침
- 특정 비지니스 로직에서는 에러 발생한 시점까지 데이터가 저장되야 하는데, 이런 것까지 모두 rollback 됨

 

 

 

 

참고:

- [스프링 부트 시작하기: 차근차근 따라 하는 단계별 실습]

반응형
Comments