[SpringDB1] 스프링과 문제 해결 - 트랜잭션 템플릿
문제 인지
트랜잭션을 사용하는 로직을 살펴보면 같은 패턴이 반복되는 것을 확인할 수 있다.
트랜잭션 사용 코드
// 트랜잭션 시작
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
// 비즈니스 로직
bizLogic(fromId, toId, money);
transactionManager.commit(status);
} catch(Exception e) {
transactionManager.rollback(status);
throw new IllegalStateException(e);
}
- 트랜잭션을 시작하고, 비즈니스 로직을 실행하고, 성공하면 커밋하고, 예외가 발생해서 실패하면 롤백한다.
- 다른 서비스에서 트랜잭션을 시작하려면
try
,catch
,finally
를 포함한 성공시 커밋, 실패시 롤백 코드가 반복될 것이다. - 이런 형태는 각각의 서비스에서 반복된다. 달라지는 부분은 비즈니스 로직 뿐이다.
- 이럴 때 템플릿 콜백 패턴을 활용하면 이런 반복 문제를 깔끔하게 해결할 수 있다.
트랜잭션 템플릿
템플릿 콜백 패턴을 적용하려면 템플릿을 제공하는 클래스를 작성해야 하는데, 스프링은 TransactionTemplate
라는 템플릿 클래스를 제공한다.
TransactionTemplate
public class TransactionTemplate {
private PlatformTransactionManager transactionManager;
public <T> T execute(TransactionCallback<T> action){..}
void executeWithoutResult(Consumer<TransactionStatus> action){..}
}
execute()
: 응답 값이 있을 때 사용한다.executeWithoutResult()
: 응답 값이 없을 때 사용한다.
MemberServiceV3_2
/**
* 트랜잭션 - 트랜잭션 템플릿
*/
@Slf4j
public class MemberServiceV3_2 {
private final TransactionTemplate txTemplate;
private final MemberRepositoryV3 memberRepository;
public MemberServiceV3_2(PlatfromTransactionManager transactionManager,
MemberRepositoryV3 memberRepository) {
this.txTemplate = new TransactionTemplate(transactionManager);
this.MemberRepository = memberRepository;
}
public void accountTransfer(String formId, String toId, int money) throws SQLException {
txTemplate.executeWithoutResult((status) -> {
try {
// 비즈니스 로직
bizLogic(fromId, toId, money);
} catch (SQLException e) {
throw new IllegalStateException(e);
}
});
}
...
}
TransactionTemplate
를 사용하려면transactionManager
가 필요하다.- 생성자에서
transactionManager
를 주입 받으면서TransactionTemplate
를 생성했다.
트랜잭션 템플릿 사용 로직
- 트랜잭션 템플릿 덕분에 트랜잭션을 시작하고, 커밋하거나 롤백하는 코드가 모두 제거되었다.
- 트랜잭션 템플릿의 기본 동작은 다음과 같다.
- 비즈니스 로직이 정상 수행되면 커밋한다.
- 언체크 예외가 발생하면 롤백한다. 그 외의 경우 커밋한다.
- 코드에서 예외를 처리하기 위해
try~catch
가 들어갔는데,bizLogic()
메서드를 호출하면SQLException
체크 예외를 넘겨준다. 해당 람다에서 체크 예외를 밖으로 던질 수 없기 때문에 언체크 예외로 바꾸어 던지도록 예외를 전환했다.
테스트 코드는 기존 코드와 동일하다. 테스트 코드 참조
정리
- 트랜잭션 템플릿 덕분에 트랜잭션을 사용할 때 반복하는 코드를 제거할 수 있었다.
- 하지만 서비스 로직인데 비즈니스 로직 뿐만 아니라 트랜잭션을 처리하는 기술 로직이 함께 포함되어 있다.
- 이렇게 비즈니스 로직과 트랜잭션을 처리하는 기술 로직이 한 곳에 있으면 두 관심사를 하나의 클래스에서 처리하게 된다. 결과적으로 코드를 유지보수하기 어려워진다.
- 서비스 로직은 가급적 핵심 비즈니스 로직만 있어야 한다.
- 이는 다음 내용으로 이어진다.트랜잭션 문제 해결 - 트랜잭션 AOP
댓글남기기