본문 바로가기
SpringBoot/(책)스프링부트 시작하기

[springboot] 8. 트랜잭션 적용하기

by 평범한kiki 2023. 4. 24.

* Transaction 사용하기
- 스프링에서 트랜잭션은 xml 설정과 어노테이션을 이용하는 방식
  그리고 AOP를 이용하는 방식
- 트랜잭션은 ACID 속성. 원자성/Rollback
- 스프링에서 코드기반 트랜잭션과 선언적 트랜잭션 처리를 지원
  선언적 트랜잭션 처리는 설정파일이나 어노테이션을 이용해서 트랜잭션을 처리하는 것을 의미

* 방법1] @Transaction 어노테이션을 이용해 트랜잭션 설정
1. /board/src/main/java/board/configuration/DatabaseConfiguration.java

- @EnableTransactionManagement  //스프링에서 제공하는 어노테이션 기반 트랜잭션을 활성화

package board.configuration;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

@Configuration
@PropertySource("classpath:/application.properties") //설정파일 위치지정
@EnableTransactionManagement  //스프링에서 제공하는 어노테이션 기반 트랜잭션을 활성화
public class DatabaseConfiguration {
	
	@Autowired
	private ApplicationContext applicationContext;
   .....
	
	@Bean
	public PlatformTransactionManager transactionManager() throws Exception{
		return new DataSourceTransactionManager(dataSource());
	}
}


2. 트랜잭션을 처리하기 원하는 곳에 @Transactional 어노테이션 추가
- @Transactional 어노테이션은 인터페이스,클래스, 메소드에 사용. 어노테이션이 적용된 대상은 설정된 트랜잭션 빈에 의해서 트랜잭션 처리

@Transactional
@Slf4j
@Service
public class BoardServiceImpl implements BoardService{
  ....
}


* 방법2]  AOP를 이용해 트랜잭션 설정

/board/src/main/java/board/aop/TransactionAspect.java 생성

- //트랜잭션의 rollback룰을 설정. 예외가 일어나면 롤백수행
  transactionAttribute.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));

package board.aop;

import java.util.Collections;

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;

@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); //트랜잭션 이름 설정
		//트랜잭션의 rollback룰을 설정. 예외가 일어나면 롤백수행
		transactionAttribute.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
		source.setTransactionAttribute(transactionAttribute);
		return new TransactionInterceptor(transactionManager, source);
	}
	
	@Bean
	public Advisor transactionAdvisor() {
		AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
		//AOP 포인트컷 설정
		pointcut.setExpression(AOP_TRANSACTION_EXPRESSION);
		return new DefaultPointcutAdvisor(pointcut,transactionAdvice());
	}
}

방법1, 방법2를 하나의 애플리케이션에 적용하는 것은 불가능하다. 

둘중 하나 선택할것

방법1] @Transaction 어노테이션

- 특별한 설정없이 쉽게 사용, 원하는 곳에 트랜잭션 설정으로 성능에 대한 영향을 최소화가능

- 어노테이션이 누락되거나 여러 메소드에 걸쳐서 사용될 경우 트랜잭션이 적용되지 않을수 있다.

   외부 라이브러리에는 적용 불가

방법2]  AOP를 이용해 트랜잭션 설정

- 공통으로 적용되어 트랜잭션 누락안됨.  외부 라이브러리에는 적용 가능

- 원하는 곳에만 트랜잭셔능ㄹ 적용하기 어려움. 트랜잭션이 필요없는 곳까지 적용되어 성능에 대한 영향