관리 메뉴

공부한것들을 정리하는 블로그 입니다.

토비의스프링3.1 5장. 서비스 추상화 본문

Spring/공부

토비의스프링3.1 5장. 서비스 추상화

호 두 2022. 7. 23. 01:06
반응형

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

5.1 사용자 레벨 관리기능 추가하기
지금까지 다루었던 DAO는 단순 DB에 저장하고 불러오는 기능만을 담당했다. 간단한 비즈니스 로직을 추가하는 것이 이 장의 목표다.

ENUM
 - 사용자 수정기능 추가

사용자 관리 로직은 어디에 두어야 할까?
 - UserService 구현하기
 - 유저 레벨 조작 구현하기
 - 처음 가입한 회원은 BASIC 등급이어야 한다.
 - 코드 리팩토링
 - 업그레이드 가능한지 여부를 확인하는 메서드
 - 실질적 레벨 업그레이드를 수행하는 메서드



5.2 트랜잭션 서비스 추상화
원본 코드를 수정하는 것은 좋은 생각이 아니다. 대신 UserService를 상속받고, upgradeLevel을 override하여 구현하면 장애상황에서의 대응을 테스트하는 좋은 UserService가 된다.

트랜잭션
 - 롤백
 - 커밋


JDBC의 트랜잭션
 - autocommit 옵션
 - try-catch : 트랜잭션 commit or rollback

첫 번째 아이디어 : 커넥션 관리를 UserService에 맡기기
upgradeLevels() 메서드 안에 커넥션의 종료와 시작을 수행해야 한다.
 
 - Class UserService
   - upgradeLevels 메서드 : Connection 객체 생성 후 try-catch문 안에서 upgradeLevel 메서드 호출
   - upgradeLevel(Connection, User) 메서드 : userDao의 update 메서드 호출
 - interface UserDao
   - update(Connection, User) : DB를 업데이트하는 책임을 가지는 update 메서드를 가진다
 - Class UserDaoJDBC implements UserDao
   - update(Connection, User) : DI받은 Connection을 이용해 DB업데이트 역할을 수행하는 서비스로직 작성

=> 트랜잭션 문제는 해결되었으나, 추가 문제점 발생
 - 템플릿 패턴(JdbcTemplate)을 사용하기 전으로 되돌아가서, JDBC API를 직접 사용하는 초기 방식
 - UserService에서 DAO의 메소드와 비즈니스 로직의 파리미터에 Connection 이 추가되었기 떄문에
   - upgradeLevels 메서드에서 호출하는 모든 메서드(upgradeLevel)에서 DB작업이 실행되기까지, 그 사이의 모든 메소드에 걸쳐서 Connection 오브젝트가 계속 전달되어야 한다.
   - UserService는 스프링 빈으로 선언해서 싱글톤으로 되어 있으므로, 인스턴스 변수에 이 Connection을 저장해뒀다가 다른 메소드에서 재사용하는 것도 불가 (멀티스레드)
   - UserDao 인터페이스 메소드에서 Connection 오브젝트를 파라미터로 가지게 된 순간, 결합도가 오르고 응집도가 낮아지고(안티패턴) DI를 적용한 내용이 쓸모없게 됨
 

두 번째 아이디어 : 독립적인 트랜잭션 동기화
DAO 메서드가 이 Connection을 사용하도록 한다.
 - Connection 파라미터 제거
 - 트랜젝션 동기화 관리자를 이용해 동기화 작업을 초기화
 - DataSourceUtils에서 DB커넥션을 생성하고 트랜잭션을 시작
   - 파라미터에서 Connection 제거 가능

멀티쓰레드 환경에서도 안전하기 트랜잭션 구현하기
 - DataSource에서 직접 가져오는게 아닌, DataSource유틸리티를 만들어서 Connection을 획득하도록 작성함으로써 ThreadSafe한 저장소에 바인딩 구현
 - 하나의 커넥션으로 반복적인 쿼리 실행

JdbcTemplate와 트랜잭션 동기화
 - DataSource유틸리티를 통해 커넥션을 새로 생성했다면 새로운 커넥션을 쓰도록 구현


트랜젝션 서비스 추상화
 - 기술과 환경에 종속되는 트랜젝션 경계설정 코드

시나리오 1
 - 여러 DB를 하나의 트랜젝션에 처리하는 경우
 - JDBC Connetion을 이용한 로컬 트랜젝션은 하나의 DB에 종속되기 때문에, 별도의 트랜젝션 관리자를 이용한 글로벌 트랜젝션 사용 필요
 - JDBC 이외의 글로벌 트랜젝션을 제공하는 Java Transaction API가 있다.
 - 트랜젝션 매니저는 DB와 각 서버를 제어하고 관리하는 리소스 매니저와 XA 프로토콜을 이용해 연결된다.
   - 어플리케이션은 JTA를 통해 다수 DB와 서버를 관리할 수 있다.


5.3 서비스 추상화와 단일 책임 원칙
수평-수직 계층구조와 의존관계
 - UserDao-UserService의 분리는 같은 계층에서 비즈니스 로직과 데이터 접근 로직을 분리한 수평적인 분리라면, 
 - 트랜잭션의 추상화는 애플리케이션 레벨의 로직과 로우레벨의 트랜잭션 기술에 독립적인 코드를 분리한 것이다.

단일 책임 원칙



5.4 메일 서비스 추상화
 - UserService의 upgradeLevel()에 메일 발송 기능을 추가

악명높은 JavaMail의 설계

테스트와 서비스 추상화
 - 스프링이 제공하는 MailSender를 구현한 추상화 클래스를 이용하면, JavaMail이 아닌 다른 메세징 서버 API를 이용하고자 할 때도 MailSender를 구현한 클래스를 만들어 DI해주면 끝나는 일이다.
 - 만약 DB의 트랜젝션 개념을 메일에 적용한다면?
   - 메일을 업그레이드할 유저 목록을 별도의 목록에 저장하고, 업그레이드 작업이 성공적으로 끝마쳤다면 한 번에 전송하도록 한다
   - 유사 트랜잭션을 구현한다. send() 메서드를 호출하더라도 실제로 메일을 발송하지 않고 있다가, 작업이 끝나면 메일을 모두 발송하고, 예외가 발생한다면 메일 발송을 취소하는 방법으로 구현한다

테스트 대역
 - 테스트 스텁(Test Stub)
 - 테스트에 깊게 관여하는 테스트 대역



반응형
Comments