보통 애플리케이션 구조는 크게 Controller/Service/Repository 로 나뉜다.
우리가 눈여겨 볼곳은 Service단인데 Service단은 주로 핵심 비즈니스 로직이 들어있는 곳이다. 또한 데이터 저장 기술을 변경해도, 비즈니스 로직은 최대한 변경없이 유지해야 한다.
이렇게 만들려면 서비스 계층을 특정 기술에 종속적이지 않게 하여야 한다.
=> 서비스 계층은 가급적 비즈니스 로직만 구현하고 특정 구현 기술에 직접 의존해서는 안된다. 이렇게 하면 향후 구현 기술이 변경될 때 변경의 영향 범위를 최소화 할 수 있다.
트랜잭션은 비즈니스 로직이 있는 서비스 단에서 하는것이 좋다. 하지만 대략적으로만 봐도 트랜잭션을 사용하기위한 JDBC 기술들 (Connection/DataSource/SqlException/) 에 너무 의존하고 있는게 보인다.
어쩌면 비즈니스 로직보다 트랜잭션 처리를 위한 JDBC기술들이 더 많아보여 주객전도가 된 느낌이다.
나중에 데이터접근 기술이 변경이되면 JDBC기술들 또한 다 바꿔야하며 그에따른 비즈니스 로직도 함께 수정하는 문제가 발생한다. 즉 유지보수가 어렵다는 문제점이 발생한다.
1.서비스 계층은 순수해야한다
2.트랜잭션 동기화 문제
3.트랜잭션 반복문제 (try,catch,finally)
스프링은 트랜잭션 동기화 매니저를 제공한다.
간략하게 flow를 설명하면 트랜잭션 매니저에서 dataSource를 통해 커넥션을 획득하고 트랜잭션을 시작한다. 이때 획득한 트랜잭션 커넥션을 트랜잭션 동기화 매니저에 안전하게 저장한다
이후로 트랜잭션 커넥션이 필요할떄마다 트랜잭션 동기화 매니저에 저장되어있는 커넥션을 가져다가 사용하면 된다.
트랜잭션 동기화 매니저가 관리하는 커넥션이 있으면 그것을 반환하고 없으면 새로운 커넥션을 생성해서 반환.
새로운 커넥션순서는 위에서 설명 햇듯이 DataSource를 통해 커넥션을 획득하고 트랜잭션을 시작한다. 그리고 그 커넥션을 트랜잭션 동기화 매니저에 저장
리소스를 닫을때도 DataSoruceUtils 사용 -> 주의해야할점은 트랜잭션 커넥션은 유지시켜주고 일반 커넥션은 닫아준다.
트랜잭션 매니저를 주입받은뒤 사용해주면된다. 사용법은 사진에서 보듯이 간단하다.
자동커밋으로 바꿔주고, 리소스를 닫는 일들은 트랜잭션 매니저가 알아서 해준다.
트랜잭션 매니저는 dataSource를 통해 커넥션이 필요함으로 파마리터로 넘겨준다. 이 부분과 레퍼지토리에서 커넥션을 가져오는 경우 (DataSourceUtils.getConnection(dataSource)) 를 헷갈리지 말자. 레파지토리에서 가져오는 부분은 이미 트랜잭션 동기화 매니저에 이미 생성된 커넥션을 가져오는 것일뿐
JDBC를 사용함으로 트랜잭션 매니저 구현체인 DataSourceTransacitonManaer을 주입한다.
=> 트랜잭션 매니저도 JDBC기술에 의존하지 않게 해주고 많이 편리해 졌지만 매번 try-catch문으로 감싸고 패턴이 같고 결국에는 반복코드이다. 이를 해결해주기위해 나온 템플릿 콜백 패턴을 살펴보자. 스프링에서는 이를 적용하기위해 트랜잭션 템플릿 이라는것을 제공해준다.
트랜잭션 템플릿을 주입받을때 트랜잭션 매니저가 꼭 필요하다.
비즈니스 로직에서 SQLException을 던지기 떄문에 try-catch 처리
커밋하고 롤백하는 코드가 모두 사라졌다. 비즈니스 로직이 정상 수행되면 커밋하고 언체크 예외가 발생하면 롤백한다. 그외의 경우 커밋한다.
=> 많은 코드가 줄고 편리해졌지만 이곳은 서비스단 이기에 되도록이면 순수 서비스코드들만 즉 핵심 비즈니스 로직만 있는것이 좋다.(지금은 트랜잭션코드와 비즈니스 로직이 함께 있다.) 기능들을 분리해야 서로 독릭접이여야 나중에 유지보수 하기가 쉬워진다.
이를 해결하기위해 트랜잭션AOP에 대해 알아보자 AOP에 대한 설명은 AOP 저번에 썼던 글이 있기에 대체한다.
스프링에서는 @Transactional이라는 애노테이션을 제공해 AOP를 사용해서 트랜잭션을 편리하게 처리하도록 도와준다.
기존 테스트 코드를 만들때는 내가 직접만들고 조립해서 테스트 하는식 즉 스프링을 전혀 사용하지 않았다. 하지만 스프링AOP 사용시 주의해야할점은 스프링에서 AOP를 제공할때 필요한것들이 따로 컨테이너에 미리 존재 해야한다.
@SpringBootTest를 사용해 테스트시 스프링부트에서 컨테이너를 만들도록 생성한다.
=> 물론 개발자가 내가 했던것처럼 직접 데이터소스나, 트랜잭션 매니저 생성가능