Spring/Spring AOP

AOP - 트랜잭션

레알윙 2020. 2. 12. 20:07
반응형

1. 메소드 분리

1-1 트랜잭션 경계설정과 비지니스 로직이 공존하는 메소드

위 코드의 특징은 트랜잭션 경계설정의 코드와 비지니스 로직 코드 간에 서로 주고받는 정보가 없는 완벽하게 독립적인 코드 

 

1-2 비지니스로직과 트랜잭션 경계설정의 분리

순수하게 사용자 레벨 업그레이드를 담당하는 비지니스 로직코드만 독립적인 메소드에 담겨있기 때문에 이해도 쉽고 수정도 부담이 없다.실수로 트랜잭션 코드를 건드릴 염려도 없어졌다.

 

위와같이 책에 적혀있는 메소드를 분리하는 로직을 직접 이해하고 사용해보니 회사에서 분리시켜야되는 메소드들이 생각나기 시작했다.... 내일 점심시간에 이 부분좀 수정해야겠다.

 

2. DI를 이용한 클래스의 분리

2-1 UserService 클래스와 클라이언트의 직접 연결을 통한 강한 결합
2-2 UserServie 인터페이스 도입을 통한 약한 결합을 갖는 유현한 구조

그림 1-2 구조는 2-1 그림과 같이 강한 결합도로 고정되어 있다. 그래서 위의 결합도를 약하게 만들기 위하여 UserSerice를 인터페이스로 만들고 기존 코드는 UserService인터페이스의 구현클래스를 만들게 된다면 클라이언트와 결합이 약해지고 직접 구현 클래스에 의존하고 있지 않기 때문에 그림 2-2 처럼 유현한 확장이 가능해진다.

 

 

 

 

2-3 트랜잭션 경계설정을 위한 UserServiceTx 도입

 

UserService를 구현한 또다른 구현 클래스를 만드는 이유는 구현현 클래스인 UserServiceImpl을 대신하기 위해 만든 것이 아니라 트랜잭션의 경계설정이라는 책임을 맡고 있게 하였다. 그리고 스스로는 비지니스 로직을 담고 있지 않기 때문에 또라느 비지니스 로직을 담고 있는 UserService의 구현 클래스에 실제적인 로직 처리 작업을 위임하는 것이다. 그 위임을 위한 호출 작업 이전과 이후에 적절한 트랜잭션 경계를 설정해두면 클라이언트 입장에서 볼때는 결국 트랜잭션이 적용된 비지니스 로직의 구현의 동작이 실행 된다.

 

그림 2-3 기준으로 한 코드

트랜잭션 코드를 제거한 UerService 구현클래스

public class UserServiceImpl implements UserService {
	UserDao userDao;
	MailSender mailSender;

	public void upgradeLevels()  {
		List<User> users = userDao.getAll();
		for(User user : users) {
			if(canUpgradeLevel(user)) {
				upgradeLevel(user);
			}
		}
	}

	private void upgradeLevelsInternal() {
		List<User> user = userDao.getAll();
		for (User user : users) {
			if (canUpgradeLevel(user)) {
				upgradeLevel(user);
			}
		}
	}
}

 

비지니스 트랜잭션 처리를 담은 UserServiceTx

위임 기능을 가진 UserServiceTx클래스

public class UserServiceTx implements UserService{
	UserService userService;
	
	public void setUserService(UserService userService) {
		this.userService = userService;
	}
	
	public void add(User user) {
		userService.add(user);_
	}
	
	public void upgradeLevels() {
		userService.upgradeLevels();
	}
}

 

트랜잭션이 적옹된 UserServiceTx

public class UserServiceTx implements UserService{
	UserService userService;
	PlatformTransactionManager transactionManager;
	
	public void setTransactionManager(PlatformTransactionManager transactionManager) {
		this.transactionManager = transactionManager;
	}
	
	public void setUserService(UserService userService) {
		this.userService = userService;
	}
	
	public void add(User user) {
		userService.add(user);_
	}
	
	public void upgradeLevels() {
		TransactionStatus status = this.transactionManager
								.getTransaction(new DefaultTransactionDefinition());
		try {
			userService.upgradeLevels();
			this.transactionManager.commit(status);
		}catch(RuntimeException e) {
			this.transactionManager.rollback(status);
			throw e;
		}
		userService.upgradeLevels();
	}
}

 

 

위의 코드처럼 만들면 생기는 장점(트랜잭션을 DI를 통해 개발하면)

  • 비지니스 로직을 담당하고 있는 UserServiceImpl의 코드를 작성할 때는 트랜잭션과 같은 기술적인 내용에는 전혀 신경 쓰지 않아도 된다.
    • 스프링의 JDBC나 JTA 같은 로우레벨의 트랜잭션 API는 물론이고 스프링의 트랜잭션 추상화 API조차 필요없음
  • 비지니스 로직에 대한 테스트를 쉽게 만들어 낼 수 있음

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

출처

토비의 스프링 3.1

반응형