일단 기본적인 JDBC, DataSource, Connection Pool 등에 대한 정리는

 

https://minwan1.github.io/2017/04/08/2017-04-08-Datasource,JdbcTemplate/

 

Wan Blog

WanBlog | 개발블로그

minwan1.github.io

이곳에 무지 잘 설명돼 있으니, 여기를 살펴보자.

 

 

 트랜잭션이란 더 이상 나눌 수 없는 단위 작업을 말한다. 전체가 다 성공하든지 아니면 전체가 다 실패해야 한다. 중간에 예외가 발생해서 작업을 완료할 수 없다면 아예 작업이 시작되지 않은 것처럼 초기 상태로 돌려놔야 한다. 이것이 트랜잭션이다.

 DB는 그 자체로 트랜잭션을 지원한다. 하나의 SQL 명령을 처리하는 경우는 DB가 트랜잭션을 보장해준다고 믿을 수 있다. 하지만 여러 개의 SQL이 사용되는 작업을 하나의 트랜잭션으로 취급해야 하는 경우가 있다. 중간에 실패가 발생해서 그 전에 처리한 SQL 작업까지 모두 취소하는 작업을 트랜잭션 롤백이라고 한다.

 반대로 SQL이 모두 성공적으로 마무리 됐으면 DB에 알려줘서 작업을 확정시켜야 한다. 이것을 트랜잭션 커밋이라고 한다.

 

 JDBC 트랜잭션의 트랜잭션 경계설정.

 

모든 트랜잭션은 시작하는 지점과 끝나는 지점이 있다. 애플리케이션 내에서 트랜잭션이 시작되고 끝나는 위치를 트랜잭션의 경계라고 부른다. JDBC 의 트랜잭션은 하나의 Connection을 가져와 사용하다가 닫는 사이에서 일어난다. 트랜잭션의 시작과 종료는 Connection 오브젝트를 통해 이뤄진다. 자동 커밋을 꺼주면, 트랜잭션이 한 번 시작되면 commit() 혹은 rollback() 메소드가 호출될 때까지의 작업이 하나의 트랜잭션으로 묶인다. 이렇게 setAutoCommit(false) 로 트랜잭션의 시작을 선언하고 commit() 또는 rollback()으로 트랜잭션을 종료하는 작업을 트랜잭션의 경계설정이라고 한다.

 트랜잭션의 경계난 하나의 Connection이 만들어지고 닫히는 범위 안에 존재한다는 점도 기억해두자. 이렇게 하나의 DB 커넥션 안에서 만들어지는 트랜잭션을 로컬트랜잭션이라고 한다.

 

 

 JdbCTemplate은 하나의 템플릿 메소드 안에서 Datasource의 getConnection() 메소드를 호출해서 Connection 오브젝트를 가져오고, 작업을 마치면 Connection을 확실하게 닫아주고 템플릿 메소드를 빠져나온다. 결국 템플릿 메소드 호출 한 번에 한 개의 DB 커넥션이 만들어지고 닫히는 일까지 일어난다. 따라서 템플릿 메소드가 호출될 때마다 트랜잭션이 새로 만들어지고 메소드를 빠져나오기 전에 종료된다.

 이러면, 각 메소드들이 JdbcTemplate을 사용한다고 가정하면, 여러 메소드들을 하나의 트랜잭션으로 묶을 수 없다.

 

따라서 하나의 트랜잭션으로 묶기위해 직접 DB Connection을 생성하고, 트랜잭션을 시작하고, Connection 오브젝트를 각 메소드들에게 전달하는 방식을 생각할 수 있다. (마지막에는 당연히 Connection을 종료해준다.)

 하지만 이 또한 여러 문제점이 있다. 

 1. JdbcTemplate을 사용할 수 없으며 try catch finally 가 반복된다.

 2. Connection 파라미터가 계속 전달되게 생겼다. 지저분하다.

 3. Connection 파라미터가 추가되면서 더이상, 파라미터를 사용하는 객체는 데이터 액세스 기술에 "독립적일 수 없다."

 4. 테스트에도 영향을 끼친다.

...등등의 문제가 있다.

 

트랜잭션 동기화

 

그러면 Connection을 파라미터로 직접 전달하는 문제를 해결해보자. 트랜잭션 경계설정은 무슨 수를 쓰던 하긴 해야 한다. 하지만 Connection을 메소드의 파라미터로 전달하는건 피하고 싶다. 이를 위해 스프링이 제안하는 방법은 독립적인 트랜잭션 동기화 방식이다. 트랜잭션 동기화란, 트랜잭션을 시작하기 위해 만든 Connection Object를 특별한 저장장소에 보관해두고 이후에 호출되는 DAO의 메소드에서는 저장된 Connection을 가져다가 사용하게 하는 것이다. 

 [트랜잭션 동기화 저장소의 활용]

 트랜잭션 동기화 저장소는 작업 스레드마다 독립적으로 Connection 오브젝트를 저장하고 관리하기 때문에 다중 사용자를 처리하는 서버의 멀티스레드 환경에서도 충돌이 날 염려는 없다.

 

 스프링이 제공하는 트랜잭션 동기화 관리 클래스는 TransactionSynchronizationManager다. 이 클래스를 이용해 먼저 트랜잭션 동기화 작업을 초기화하도록 요청한다.

 

 

트랜잭션 서비스 추상화

 

 그런데 만약 DB를 여러개 사용하고, 여러개의 DB 사이에서 이뤄지는 작업을 하나의 트랜잭션으로 묶으려면 어떻게 해야 할까? JDBC의 Connection을 이용한 트랜잭션 방식인 로컬트랜잭션으로는 불가능하다. 왜냐하면 로컬 트랜잭션은 하나의 DB Conenction 에 종속되기 때문이다. 따라서 각 DB와 독립적ㅇ로 만들어지는 Connection 을 통해서가아니라, 별도의 트랜잭션 관리자를 통해 트랜잭션을 관리하는 "글로벌 트랜잭션(global tranaction)" 방식을 사용해야 한다. 글로벌 트랜잭션을 적용해야 트랜잭션 매니저를 통해 여러 개의 DB가 참여하는 작업을 하나의 트랜잭션으로 만들 수 있다. 자바는 JDBC 외에 이런 글로벌 트랜잭션을 지원하는, 트랜잭션 매니저를 지원하기 위한 API인 JTA(Java Transaction API) 를 제공하고 있다.

 

 

애플리케이션은 기존의 방식대로 DB는 JDBC를, 메시징 서버라면 JMS 같은 API를 사용해서 필요한 작업을 수행한다. 단, 트랜잭션은 JDBC나 JMS API를 직접 제어하지 않고 JTA를 통해 트랜잭션 매니저가 관리하도록 위임한다.

 

 그런데 이러면 하나의 DB만 쓰는 고객을 위해서는 JDBC를 이용한 트랜잭션 관리 코드를, 다중 DB를 위한 글로벌 트랜잭션을 필요로 하는 곳에서는 JTA를 이용한 트랜잭션 관리 코드를 적용해야 하는 상황이 된다. 또한.. 하이버네이트 같은 경우 Connection 을 직접 사용하지 않고, Session이란 것을 사용하며 독자적인 트랜잭션 관리 API를 사용한다.

 

 결국은 Connection, UserTransaction, Session/Transaction API 등에 종속되지 않게 해야한다.

 다행히도 트랜잭션의 경계설정을 담당하는 코드는 일정한 패턴을 갖는 유사한 구조다. 공통점을 뽑아서 분리시킬 수 있다.(추상화) 이렇게 하면 하위시스템이 어떤 것인지 알지 못해도, 또는 하위 시스템이 바뀌더라도 일관된 방식으로 접근할 수 있다.

 

 트랜잭션의 경계설정을, 어떤 상황에 오던 일관적으로 하기 위해서, 트랜잭션의 경계설정을 담당하는 코드를 추상화하자. 이렇게 하면 하위 시스템이 어떤 것인지 알지 못해도, 또는 하위 시스템이 바뀌더라도 일관된 방법으로 접근할 수 있다.

 

 

스프링의 트랜잭션 서비스 추상화

 

스프링은 트랜잭션 기술의 공통점을 담은 트랜잭션 추상화 기술을 제공하고 있다. 이를 이용하면 애플리케이션에서 직접 각 기술의 트랜잭션 API를 이용하지 않고도, 일관된방식으로 트랜잭션을 제어하는 트랜잭션 경계설정 작업이 가능해진다.

 

스프링이 제공하는 트랜잭션 경계설정을 위한 추상 인터페이스는 PlatformTransactionManager다. 

 

 JDBC 를 이용하는 경우에은 먼저 Connection 을 생성하고 나서 트랜잭션을 시작했다. 하지만PlatformTransactionmanager 에서는 트랜잭션을 가져오는 요청인 getTransaction() 메소드를 호출만 하면 된다. 필요에 따라서 트랜잭션 매니저가 DB 커넥션을 가져오는 작업도 같이 수행해주기 때문이다.

 

 ** PlatformTransactionManager 로 시작한 트랜잭션은 트랜잭션 동기화 저장소에 저장된다. PlatformTransactionManager를 구현한 DatasSourceTransactionManager 오브젝트는 JdbcTemplate에서 사용될 수 있는 방식으로 트랜잭션을 관리한다. 따라서 PlatformTransactionManager를 통해 시작한 트랜잭션은 JdbcTemplate안에서 사용된다.

 

 

 -> 트랜잭션 방법에 따라 비즈니스 로직을 담은 코드가 함께 변경되면 단일 책임 원칙에 위배되며, DAO가 사용하는 특정 기술에 대해 강한 결합을 만들어낸다.

 

 

 

코드에 만족하지 말고 계속 개선하려고 노력하자.

DI는 모든 스프링 기술의 기반이 되는 핵심 엔진이자 원리이다. 

스프링을 DI 프레임워크라 부르는 이유는 외부 설정정보를 통한 런타임 오브젝트 DI라는 단순한 기능을 제공하기 때문이 아니다. 오히려 스프링이 DI에 담긴 원칙과 이를 응용하는 프로그래밍 모델을 자바 엔터프라이즈 기술의 많은 문제를 해결하는 데 적극적으로 활용하고 있기 때문이다.

 

 

 

 

블로그 이미지

맛간망고소바

,