[토비의 스프링] 5장 서비스 추상화

앞쪽의 내용은 유저등급을 올리는 upgradeLevels() 메서드의 리팩터링 과정

 

비즈니스 로직에서의 경계설정

유저들의 등급을 올리다가 하나에 문제가 생기면 전체를 롤백해달라는 요구사항에 대응해야한다.

현재구조는 UserDao의 메서드가 사용될때마다 JdbcTemplate에 의해 트랜잭션이 생겼다 커밋됐다 반복함으로 롤백을 시킬수가 없어 어쩔수 없이 트랜잭션 경계를 UserService에서 설정해야한다.

public void upgradeLevels() throws Exception {
    Connection c = dataSource.getConnection();

    try {
        // 유저 등급 업그레이드 비지니스 로직
        user.upgradeLevel();
        userDao.update(c, user); // jdbcTemplate에서 해당 커넥션을 이용하게 전달
    } catch (SQLException e) {
        throw e;
    }
    ..
}

 

요구사항 대응을 위해 이런식으로 수정하면 아래와 같은 단점들이 발생한다.

1.  Connection 직접 다뤄야해서 JdbcTemplate 사용 불가

초반에 jdbc api를 직접사용했던것 처럼 비즈니스로직에서 설정한 트랜잭션을 받아서 그것을 사용해서 db에 접근해야함으로

유저뿐만 아니라 열심히 리팩터링한 UserDao 코드까지 롤백해야하는 슬픈상황이다.

2. Connection 파라미터 계속 물고다님

UserDao 까지 Connection을 전달해줘야 하기 때문에 중간에 거치는 모든 함수들의 파라미터의 Connection 이 추가되어 지저분해진다.

3. Connection 파라미터가 dao에 들어가는 순간 기술 독립적이지 않음

4장에서 예외도 래핑하고 UserDao도 인터페이스로 뽑으며 기술독립적이게 하기위해 노력했지만 메서드 시그니쳐에 Jdbc기술인 Connection이 들어가 버렸으니 추상화도 실패하게 된다.

 

개선

트랜잭션 동기화로 파라미터 제거

스프링에서 제공하는 스레드에 안전한 TransactionSychronizationManger 를 이용해서 비즈니스 로직에서 커넥션을 만들어 저장해두고 UserDao에서 꺼내쓰는 방식으로 파라미터 드릴링(?) 을 제거한다. JdbcTemplate은 트랜잭션 동기화 저장소에서 커넥션이나 트랜잭션을 먼저 찾기때문에 UserDaoJdbc 는 수정하지 않아도 된다.

public void upgradeLevels() throws Exception {
    TransactionSynchronizationManager.initSynchronization();
    Connection c = DataSourceUtils.getConnection(dataSource);
    c.setAutoCommit(false);

    try {
        // 유저 등급 업그레이드 비지니스 로직
        c.commit();
    } catch (Exception e) {
        c.rollback();
        throw e;
    } finally {
        DataSourceUtils.releaseConnection(c, dataSource);
        TransactionSynchronizationManager.unbindResource(this.dataSource);
        TransactionSynchronizationManager.clearSynchronization();
    }
}

 

트랜잭션 기술을 추상화

파라미터는 제거했지만 여전히 Jdbc 의존적이다. JdbcTemplate을 쓰지않는 UserDao 구현체로 바꾸면 잘 동작하지 않을 것이다.

예를들어 Connection 말고 Session을 쓰는 Hibernate를 사용한다거나 글로벌 트랜잭션을위해서 JTA를 쓰는 경우이다.

 그래서 트랜잭션 사용자체를 추상화해야한다. Spring에서는 여러가지 트랜잭션 관리방식을 추상화 해놓은 인터페이스 PlatformTransactionManager 를 제공한다. 이를 주입받아 사용할 수 있다.

public class UserService {
    private PlatformTransactionManager transactionManager;

    public void setTransactionManager(PlatformTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }

    public void upgradeLevels() throws Exception {
        TransactionStatus status = this.transactionManager.getTransaction(new DefaultTransactionDefinition());

        try {
            // 유저 등급 업그레이드 비지니스 로직
            this.transactionManager.commit(status);
        } catch (Exception e) {
            this.transactionManager.rollback(status);
            throw e;
        }
    }
}

 

UserDao 애플리케이션 컨텍스트에 등록할때 PlatformTransactionManager도 알맞은 구현체를 등록해주어 DI 하면 된다.

 

서비스 추상화 응용

스프링에서 제공하는 추상화기능이 없는 기술들은 직접 추상화 할수밖에 없다. 책의 예제에선 JavaMail을 MailSender라는 인터페이스로 추상화해서

1. 큐를 사용하는 메일전송기능을 만든다거나

2. 테스트 대역(DummyMailSender)을 만들어 테스트를 더 용이하게 해주는 방법도 소개해준다.

'개발 > spring' 카테고리의 다른 글

[토비의 스프링] 6장 AOP (2)  (0) 2024.03.08
[토비의 스프링] 6장 AOP (1)  (0) 2024.02.26
[토비의 스프링] 4장 예외  (0) 2024.02.15
[토비의 스프링] 3장 템플릿  (0) 2024.02.06
[토비의 스프링] 2장 테스트  (0) 2024.01.30