[Spring 3.1] 트랜잭션과 서비스 추상화
스프링은 트랜잭션을 구현할 때 TransactionSynchronizations 방식을 사용한다.
Connection 객체를 특별한 저장소에 보관해두고 이후 하나의 트랜잭션 내부에서 호출되는 다른 메서드를 수행할 때는 저장소에 저장된 Connection을 가져가서 사용하는 방식이다.
Service에서 Connection을 생성하고 저장소에 Connection을 저장한 후 트랜잭션을 시작한다.
이후 서비스의 메서드를 수행할 때는 저장소에 Connection 객체가 있는지 확인한다.
트랜잭션 내부의 작업이 모두 끝났을 때 커밋을 호출해 트랜잭션을 완료시킨 후 Connection을 제거한다.
작업 스레드마다 독립적으로 Connection을 사용하기에 멀티스레드 환경에서도 충돌이 나지 않는다.
JdbcTemplate은 동기화 저장소에 Connection이나 Transaction이 없는 경우 내부에서 트랜잭션과 커넥션을 만든 후 작업을 시작하고, 이미 존재한다면 가져와서 작업을 이어서 진행한다.
따라서 트랜잭션 적용 여부에 맞춰서 DAO 코드를 수정할 필요가 없다.
하지만, 하나의 트랜잭션 안에서 여러 개의 DB에 데이터를 넣는 작업을 진행하거나 DB에 접근할 때 사용되는 기술을 JPA로 변경하는 경우를 생각해보자.
첫 번째 경우에서는 Java Transaciton API 를 사용해 로컬 트랜잭션을 글로벌 트랜잭션으로 변경해야 하고, 두 번째 경우에서는 JPA만의 독자적인 트랜잭션 관리 코드를 사용해야 한다.
즉, Service 계층에서 트랜잭션 관련 설정을 추가로 진행해야 하고, 이는 Service 계층이 DB 접근 기술에 종속되는 구조를 낳는다.
트랜잭션 관련 설정을 진행하는 부분은 일정한 패턴을 가지는 유사한 구조로 구성된다.
JDBC도 데이터베이스와 통신할 때 SQL을 사용한다는 공통점을 추상화해서 만들어졌다.
트랜잭션에서도 공통점을 뽑아내서 분리시키는 추상화 작업을 수행할 수 있지 않을까?
그리고, 트랜잭션 관리 계층을 만들면 애플리케이션 코드에서는 트랜잭션 추상화 계층이 제공하는 API를 사용해 Service 계층이 특정 기술에 종속되지 않게 설계할 수 있지 않을까?
스프링은 트랜잭션 추상화 기술을 제공해 위의 문제를 해결하도록 도와준다.
PlatformTransactionManager 인터페이스를 제공하고, 개발자는 적절한 구현체를 사용해 트랜잭션 관련 설정을 진행한다.
사용할 때는 @Autowired 애너테이션으로 DI 받도록 설계하고, xml에 빈 관련 설정을 진행해주자.
이제 Service 계층은 트랜잭션 설정이 변경되더라도 수정할 필요가 없어진다.
Service와 DAO(Repository) 계층을 기능적인 관심에 따라 분리하는 작업을 수평적인 분리라고 부르고,
트랜잭션의 추상화처럼 비즈니스 로직과 설정 로직을 분리하는 작업을 수직적인 분리라고 부른다.
사진을 참고하면 이해하기 쉽다.
애플리케이션 계층 / 서비스 추상화 계층 이 분리되어있고, 애플리케이션 계층에서는 Service와 DAO가 분리되어있고, 각각을 연결할 때는 DI 기술을 사용한다.
수평적 구분이든 수직적 구분이든 서로 영향을 주지 않고 자유롭게 확장할 수 있는 구조에는 DI 기술이 강하게 연관되어있다. (Test Stub 에서도 DI를 사용한다)
잠시 정리해 보자.
Service 계층이 비즈니스 관련 로직과 트랜잭션 설정 관련 로직을 함께 처리해야 하는게 시작이였다.
Single Responsibility Principle에 의거해 하나의 모듈은 하나의 책임만을 가져야 함에 합리적이고, 이를 위해 트랜잭션을 추상화하고 추상화한 인터페이스는 서비스와 DI로 연결해 책임을 분리했다.
SRP를 지키는 코드는 변경이 필요할 때 수정할 부분을 명확하게 지정할 수 있다.
잘 와닿지 않다면 극단적으로 생각해보면 된다. DAO와 Service가 10,000,000 개 있다면? 서비스 하나가 여러 개의 DAO를 사용하게 된다.
여기서 SRP를 제대로 지키고 있지 않은 경우 하나의 DAO에 대한 수정사항이 도착했을 때 관련된 모든 Service를 수정해야 하는 상황이 발생한다 -_-
작업량 뿐만 아니라 실수와도 직결되는 문제이기에, 객체지향 설계와 프로그래밍 원칙에 입각한 애플리케이션 제작은 매우 중요하다.
변화에 효과적으로 대응하기 위해 인터페이스를 도입하고 DI로 각 계층을 연결한다.
그 과정에서 OCP, SRP를 지키는 코드를 작성하게 되고, 자연스럽게 전략 패턴 / 어댑터 패턴 등 여러 디자인패턴을 적용한다.
이렇듯 객체지향 설계와 프로그래밍 원칙은 서로 긴밀하게 연관되어있다.
좋은 코드를 설계하기 위해 어떤 방법을 사용해야 하는지... 어떻게 설계해야 하는지... 끊임없이 고민해야 한다.
스프링 프레임워크는 많은 문제를 어떻게 해결했는지 프로젝트를 받아서 소스를 하나씩 까보면서 공부하는 것도 좋은 방법이다.
'Spring > Spring 3.1' 카테고리의 다른 글
[Spring 3.1] Annotation (0) | 2023.05.04 |
---|---|
[Spring 3.1] Aspect Oriented Programming (0) | 2023.04.28 |
[Spring 3.1] 스프링과 예외처리 (0) | 2023.04.20 |
[Spring 3.1] 템플릿과 콜백 (0) | 2023.04.19 |
[Spring 3.1] 스프링과 테스트 (0) | 2023.04.16 |
댓글
이 글 공유하기
다른 글
-
[Spring 3.1] Annotation
[Spring 3.1] Annotation
2023.05.04 -
[Spring 3.1] Aspect Oriented Programming
[Spring 3.1] Aspect Oriented Programming
2023.04.28 -
[Spring 3.1] 스프링과 예외처리
[Spring 3.1] 스프링과 예외처리
2023.04.20 -
[Spring 3.1] 템플릿과 콜백
[Spring 3.1] 템플릿과 콜백
2023.04.19