토비의 스프링 3.1 정독기 - 서비스 추상화

문지은·2021년 9월 13일
0

토비의 스프링

목록 보기
5/6

트랜잭션 경계 설정

비즈니스 로직과 데이터 액세스 로직을 분리하기 위해서 서비스 계층을 따로 둔다. 이 때 서비스 계층에서 다양한 트랜잭션을 사용할 경우 원자성에 따라서 해당 로직을 하나의 트랜잭션으로 묶는 작업이 필요하다. 즉 트랜잭션 경계 설정이 필요하다!

트랜잭션 동기화 기법

이 때 트랜잭션에 대한 정보를 dao에 그 때 그 때 전달하는 방식은 매우 비효율적이다. 그렇기에 우리는 트랜잭션 동기화 기법이라는 것을 사용한다. 이 기능은 스프링에서 제공하는 트랜잭션 동기화 관리자를 이용해서 쉽게 사용할 수 있다.

private DataSource dataSource;

// dataSource를 DI 받는다.
public void setDataSource(DataSource dataSource)
{
	this.dataSource = dataSource;
}

public void upgradeLevels() throws Exception
{
	// 트랜잭션 동기화 관리자를 통해서 동기화 작업 초기화
    TransactionSynchronizationManager.initSynchronization();
    
    // DB 커넥션 생성 및 동기화
    // 이후 모든 작업은 이 트랜잭션 안에서 진행한다.
    Connection c = DataSourceUtils.getConnection(dataSource);
    c.setAutoCommit(false);
   
   	try
    {
    	.
    	.
    	.
    }
    catch (Exception e)
    {
    	// 오류 발생하면 롤백
        c. rollback();
        throw e;
    }
    finally
    {
    	// DB 커넥션 닫고 동기화 작업 종료
        DataSourceUtils.releaseConnection(c, dataSource);
        
        TransactionSynchronizationManager
        	.unbindResource(this.dataSource);
        TransactionSynchronizationManager
        	.clearSynchronization();
    }
    
}

그런데 위와 같이 DB에 접근하는 트랜잭션 코드가 클라이언트에 따라서 달라질 수 있다. 예를 들어서 갑자기 새롭게 등장한 클라이언트는 다중 DB를 사용할 수도 있다. 그러면 또 새로운 경계 설정이 필요하다. 이를 위해서 JTA가 존재한다. Java Transaction API의 줄임말로 트랜잭션을 관리하는 API이다. 트랜잭션별로 알맞은 리소스를 가져오는 역할을 한다.

하지만 또 새로운 문제가 발생할 수 있다. 이번에 등장한 새로운 클라이언트는 하이버네이트를 사용한다고 한다. 이거는 JTA로 커버칠 수가 없다! 🤦‍♀️ 어떻게 할까???

트랜잭션 서비스 추상화

하지만 위의 예시에서 클라이언트는 모두 디비에 접근해야 한다는 공통적인 특징을 가진다. 이 특징을 빼내서 추상화된 트랜잭션 관리 계층을 만들 수 있지 않을까? 이것이 트랜잭션 서비스 추상화의 첫걸음이다.

public void upgradeLevels()
{
	// 추상 오브젝트 생성
	PlatformTransactionalManager transactionManager 
    = new DataSourceTransactionManager(dataSource);
    
    // 위의 코드처럼 추상 오브젝트로 디비 접근 작업 이어서 진행 ...
    
}

추상 오브젝트는 빈으로 만들어서 DI를 받아 보다 편리하게 이용할 수 있다.

여기서 DI를 이용하면 확실한 장점이 생긴다. 바로 단일 책임 원칙을 지킬 수 있다는 것이다. 외부에서 dataSource를 지정하기 때문에 함수는 이에 대한 책임을 지지 않고 서로 영향을 주지 않게 된다.

테스트 대역

  • 테스트 대역 (test double) : 테스트할 기능에만 집중할 수 있도록 의존 오브젝트를 대체할 수 있도록 만든 오브젝트

  • 테스트 스텁 (test stub) : 테스트 대역 중 하나로 테스트 동안에 코드가 정상적으로 동작하게 하는 의존 객체

그런데 테스트 스텁 중에서 리턴값을 가지는 것도 있을 것이다. 해당 리턴 값을 저장해뒀다가 테스트 결과와 일치하는지 확인하기 위해서는 목 오브젝트를 사용해야 한다.

profile
백엔드 개발자입니다.

0개의 댓글