트랜잭션 템플릿

jooog·2022년 7월 2일
0

스프링DB

목록 보기
9/13

📒 트랜잭션 템플릿 적용

웹 애플리케이션을 만들다보면 트랜잭션을 시작하고 비즈니스 로직을 실행한다. 그리고 제대로 실행된다면 커밋하고 예외가 발생해서 실패하면 롤백하는 동일한 과정을 만들게 되는데 동일하게 반복되는 코드를 매번 작성하는 것은 상당히 비효율적이라고 할 수 있다.

주로 'try , 'catch', 'finally'를 사용하게 되는데 이런 코드에서 달라지는 부분은 비즈니스 로직이 실행되는 코드 뿐이다. 이렇게 반복되는 코드 사용을 줄여서 효율적으로 코딩할 수 있도록 도와주는 것이 트랜잭션 템플릿이다. 즉, 달라지는 서비스코드를 제외한 나머지 반복되는 트랜잭션 코드들은 하나의 템플릿을 사용하는 것이다.

TransactionStatus status = transactionManager
						.getTransaction(new DefaultTransactionDefinition());

        try {
           
           //비즈니스 로직을 실행하는 코드
            logic(fromId,toId, money );

            transactionManager.commit(status);


        } catch(Exception e) {

            transactionManager.rollback(status);
            throw  new IllegalStateException(e);

        } 

트랜잭션 코드가 반복되는 문제를 해결하기 위해 템플릿 콜백 패턴을 적용해본다.

📒 템플릿 콜백 패턴 'TransactionTemplate'

스프링이 제공하는 'TransactionTemplate' 기능을 사용하면 트랜잭션을 시작하고 커밋 또는 롤백되는 코드를 매번 작성하는 수고로움을 덜 수 있다. 템플릿 콜백 패턴을 적용하려면 템플릿을 제공하는 클래스를 작성해야 한다. 스프링에서 제공하는 'TransactionTemplate' 템플릿 클래스를 사용해보자.

 private final TransactionTemplate template;
 private final MemberRepositoryV3 memberRepository;

    public MemberServiceV3_2(PlatformTransactionManager transactionManager, 
                             MemberRepositoryV3 memberRepository) {
        this.template = new TransactionTemplate(transactionManager);
        this.memberRepository = memberRepository;
    }

TransactionTemplate에 transactionManager를 주입한다.

template.executeWithoutResult((status) -> {
            //비즈니스 로직
            try {
            
                logic(fromId,toId, money );
                
            } catch (Exception e) {
            
                throw new IllegalStateException(e);
            }
        });

template의 executeWithoutResult를 사용하고 안에 비즈니스 로직 코드만 하나 넣어준다. 그리고 예외 처리를 위해 try, catch문을 사용했다. 람다에서 체크 예외를 밖으로 던질 수 없기 때문에 언체크 예외로 바꾸어 던지도록 전환한다. 코드를 자세히 살펴보면 executeWithoutResult 안에서 트랜잭션을 시작하면 그 다음으로 비즈니스 로직이 실행된다. 비즈니스 로직이 성공적으로 실행되면 커밋을 하고 예외가 발생해 실패하면 롤백을 하는 방식으로 동작한다.

📒 트랜잭션 템플릿 이점

트랜잭션 템플릿 사용전 코드

TransactionStatus status = transactionManager
                          .getTransaction(new DefaultTransactionDefinition());

        try {
            //비즈니스 로직 수행
            //커넥션을 넘기지 않아도 된다
            logic(fromId,toId, money );

            //성공하면 커밋
            transactionManager.commit(status);

            //예외가 발생할 경우 처리
        } catch(Exception e) {

            transactionManager.rollback(status);
            throw new IllegalStateException(e);

        } //트랜잭션이 커밋되거나 롤백되면 알아서 커넥션이 종료된다

트랜잭션 템플릿 적용 코드

template.executeWithoutResult((status) -> {
            //비즈니스 로직
            try {
            
                logic(fromId,toId, money );
                
            } catch (Exception e) {
            
                throw new IllegalStateException(e);
            }
        });

기존 코드와 비교해보면 확실히 코드가 간결해진 것을 확인할 수 있다. 트랜잭션 템플릿을 적용한 코드를 보면 기존에 트랜잭션을 시작하고 커밋이나 롤백하는 코드를 직접 개발자가 작성하지 않아도 된다. 즉, 보다 간편하게 트랜잭션을 시작하고 종료할 수 있다.

📒 기존 코드의 문제점

해당 코드의 문제점이 없는 것은 아니다. 바로 핵심 로직이라고 할 수 있는 서비스 로직과 트랜잭션을 처리하는 기술 로직이 한 코드로 묶여 있다는 점이다. 이렇게 핵심 기능과 부가기능이 들어간 코드가 하나로 섞여 있으면 향후 유지보수가 어려워지게 된다. 따라서 서비스 로직은 서비스 로직으로 따로 분리되어야 할 필요가 있는데 어떻게 하면 트랜잭션과 서비스 로직을 분리해서 코드를 작성할 수 있을까? 다음에 이런 문제를 해결하는 과정을 공부해 보고자한다.

이 글은 김영한님의 스프링 DB 1편 - 데이터 접근 핵심 원리 강의를 듣고 정리한 내용입니다.

0개의 댓글