Presentation is loading. Please wait.

Presentation is loading. Please wait.

5.2 트랜잭션 서비스 추상화.

Similar presentations


Presentation on theme: "5.2 트랜잭션 서비스 추상화."— Presentation transcript:

1 5.2 트랜잭션 서비스 추상화

2 5.2.1 모 아니면 도 사용자 레벨 업그레이드 작업시에 예상치 못한 상황 (정전)발생 태스트 시나리오부터 고려
일부의 사용자는 레벨이 업그레이드 됨 일부의 사용자는 레벨이 그대로임 (업그레이드를 못함) 이런 경우 대처법? 태스트 시나리오부터 고려 장애가 발생하였을 때 일어나는 현상 중의 하나인 예외가 던져지는 상황을 의도적으로 만들어서 테스트 테스트 관점 사용자 레벨 업그레이드를 시도하다가 중간에 예외가 발생하면, 예외가 발생하기 전에 이미 업그레이드 한 사용자의 레벨이 원래로 돌아가는가?

3 5.2.1 모 아니면 도 테스트용 UserService 대역
테스트용으로 UserService를 상속하고 일부 메소드를 Override하여 테스트에 필요한 기능을 추가하여 사용하는 것이 바람직함 현재 관심이 되는 메소드: upgradeLevels() 특정 사용자(예제에서는 4번째 사용자)에 대해서는 업그레이드 하지 않고 항상 Exception 발생 상속 및 오버라이드를 위하여 기존 UserService의 upgradeLevels() 메소드는 protected로 선언 필요

4 5.2.1 모 아니면 도 테스트용 UserService 대역 클래스
테스트에서만 사용할 것이므로, 테스트 클래스 내의 내부 스태틱 클래스로 만드는 것이 간편 TestUserServiceException도 테스트 클래스의 내부 스태틱 크래스로 생성

5 5.2.1 모 아니면 도 강제 예외 발생을 통한 테스트 fail() 메소드의 역할

6 5.2.1 모 아니면 도 테스트 실패의 원인 두번째 사용자 (users.get(1))의 레벨 변경이 없음을 테스트
테스트 실행 결과 실패의 원인 upgradeLevels() 메소드가 하나의 트랜잭션 안에서 동작하지 않았기 때문 트랜잭션: 더 이상 나눌 수 없는 단위 작업 (원자성) 즉, 트랜잭션 안에서 동작하는 내용들은 모두 다 성공하던지 모두 다 실패해야 함

7 5.2.2 트랜잭션 경계설정 SQL문과 트랜잭션 하나의 SQL 문 실행 여러 SQL 문이 순차적으로 실행
항상 원자성이 보장됨. 즉, 트랜잭션이 자동으로 수행됨 여러 SQL 문이 순차적으로 실행 여러 SQL 문에 대해 원자성을 보장하기 위해서는 이들을 하나의 트랜잭션 경계 안으로 넣어주어야 함 트랜잭션 커밋 (Transaction Commit) 모든 SQL 문 수행 작업이 성공적으로 마무리 되었음을 확정함 트랜재션 롤백 (Transaction Rollback) 여러 SQL 문 수행동안 임의의 SQL 문에서 문제가 발생하면 그 이전에 수행된 모든 SQL 문들의 작업 취소 작업

8 5.2.2 트랜잭션 경계설정 JDBC 트랜잭션의 트랜잭션 경계설정 JDBC의 기본동작방식 JDBC 트랜잭션 설정
SQL 작업마다 커밋해서 트랜잭션 을 끝내버림 JDBC 트랜잭션 설정 setAutoCommit을 호출하여 기본동작 방식을 제거함 이후 아무 문제없으면 commit 처리 문제가 발생하면 rollback 처리

9 5.2.2 트랜잭션 경계설정 JDBC 트랜잭션의 트랜잭션 경계설정
트랜잭션 경계설정 (Transaction Demarcation) setAutoCommit(false)로 트랜잭션의 시작을 선언하고 commit() 또는 rollback()으로 트랜잭션을 종료하는 작업 트랜잭션의 경계는 하나의 Connection이 만들어지고 닫히는 범위 안에 존재함 로컬 트랜잭션 (Local Transaction) 하나의 DB Connection 안에 경계가 설정되는 트랜잭션

10 5.2.2 트랜잭션 경계설정 UserService와 UserDao의 트랜잭션 문제
JdbcTemplace의 기본적인 커넥션과 트랜잭션 관리 JdbcTemplace에서 관리되는 메소드 호출 한번에 커넥션이 만들어지고 닫히는 일까지 발생됨 트랜잭션은 커넥션 보다 존재 범위가 짧음 메소드 호출  커넥션 열림  트랜잭션 시작  SQL 작업 처리  트랜잭션 종료  커넥션 닫힘 및 반환 현재 테스트 내의 updateAllOrNothing() 메소드 동작 (1/2) updateLevels() 메소드 호출 updateLevels() 내부에서는 userDao.update(user) 메소드 호출 각 userDao.update() 메소드 호출마다 트랜잭션이 시작되고 작업 후 곧바로 그 트랜잭션이 종료됨 (커밋됨) 즉, 두 번째 사용자의 레벨 업데이트는 영구적으로 DB에 남아 있음

11 5.2.2 트랜잭션 경계설정 UserService와 UserDao의 트랜잭션 문제
현재 테스트 내의 updateAllOrNothing() 메소드 동작 (2/2) 즉, UserService 내에서 복잡한 비즈니스 로직을 담고 있는 하나의 메소드가 내부적으로 여러 트랜잭션으로 분화되어 수행되는 모습 해결책 UserService 내의 upgradeLevels() 메소드가 하나의 트랜잭션 경계 내에서 수행되도록 설정

12 5.2.2 트랜잭션 경계설정 비즈니스 로직 내의 트랜잭션 경계설정 (1/3)
트랜잭션 경계설정 작업을 UserService쪽으로 옮긴 코드 구조

13 5.2.2 트랜잭션 경계설정 비즈니스 로직 내의 트랜잭션 경계설정 (2/3)
동일한 트랜잭션 경계 내에서의 DB 작업은 동일한 커넥션에서 수행되어야 함 UserService에서 사용하는 UserDao의 메소드는 외부에서 Connection 객체를 받아서 사용해야 함

14 5.2.2 트랜잭션 경계설정 비즈니스 로직 내의 트랜잭션 경계설정 (3/3)
upgradeLevel 메소드까지 Connection 객체 파라미터 필요 최종적으로 수정된 UserService 클래스 내용

15 5.2.2 트랜잭션 경계설정 현재까지 구성한 UserService 트랜잭션 경계설정 문제점
1. JdbcTemplate 사용하지 못함 try/catch/finally 블록이 활용되어 코드가 지저분해짐 2. UserService내의 메소드 및 UserDao의 각 메소드들마다 Connection 객체 파라미터 선언 필요 및 해당 객체 전달 필요 Connection 변수를 인스턴스 변수로 선언하는 방법 등을 통해 공유하게 하는 방법은 절대 안됨 멀티쓰레드 환경에서 Connection이 공유되는 것은 금지 3. UserDao는 더 이상 데이터 엑세스 기술에 독립적일 수 없음 JPA나 하이버네이트는 Connection 대신 EntityManager 사용 4. UserDao를 테스트하는 테스트 코드에서도 Connection 객체를 파라미터로 넘겨주어야 하는 수정 필요

16 5.2.3 트랜잭션 동기화 Connection 파라미터 제거
트랜잭션 동기화 (Transaction Synchronization) UserService에서 트랜잭션을 시작하기 위해 만든 Connection 객체를 특별한 저장소에 보관해두고 이후에 호출되는 DAO의 메소드에서는 저장된 Connection을 가져다가 사용하게 하는 방식 트랜잭션 동기화 방식을 사용할 경우의 작업 흐름 TransactionSynchronizations: 트랜잭션 동기화 저장소 작업 쓰레드마다 독립적으로 connection 객체를 저장

17 5.2.3 트랜잭션 동기화 Connection 파라미터 제거
트랜잭션 동기화 (Transaction Synchronization) (1) UserService가 Connection 객체 생성 (2) 생성한 Connection 객체를 트랜잭션 동기화 저장소에 저장하고 Connection의 setAutoCommit(false) 호출 (4), (7), (10) 트랜잭션 동기화 저장소에 현재 시작된 트랜잭션과 연관된 Connection 객체가 존재하는 지 확인한다. (5), (8), (10) Connection사용 이후에도 닫는 작업 없이 트랜잭션 동기화 저장소에 저장해 둔다. (12) Connection의 commit()을 호출하여 트랜잭션을 완료시킨다. (13) 트랜잭션 저장소에서 해당 Connection 객체를 제거한다.

18 5.2.3 트랜잭션 동기화 트랜잭션 동기화 적용 (1/2)

19 5.2.3 트랜잭션 동기화 트랜잭션 동기화 적용 (2/2) Connection 객체를 생성하는 코드
기존: Connection c = dataSource.getConnection(); 현재: Connection c = DataSourceUtils.getConnection(dataSource); 트랜잭션 동기화 저장소에 해당 객체를 저장함 트랜잭션 동기화를 적용하여 지저분한 connection 객체 파라미터 문제를 해결함

20 5.2.3 트랜잭션 동기화 트랜잭션 테스트 보완 이전에 만든 UserServiceTest의 upgradeAllOrNothing() 메소드에 dataSource 주입 코드 필요 테스트를 수행하면 이미 변경된 사용자의 레벨들이 모두 rollback되어 원래대로 돌아간 것을 확인할 수 있음 기존 UserService 객체에도 dataSource 주입 및 관련 코딩 필요

21 5.2.3 트랜잭션 동기화 JdbcTemplate과 트랜잭션 동기화 JdbcTemplate의 3대 기능
스스로 Connection 객체 생성 및 종료 트랜잭션 동기화 저장소에 Connection 이나 트랜잭션이 존재하는 경우의 JdbcTemplate 동작 방식 TransactionSynchronizationManager.initSynchronization(); 위 코드가 실행된 이후에 JdbcTemplate을 접근하면 트랜잭션 동기화 저장소에 들어 있는 Connection을 가져와 활용함 JdbcTemplate의 3대 기능 try/catch/finally 작업 흐름 지원 SQLException 예외 변환 트랜잭션 동기화 관련 작업

22 5.2.4 트랜잭션 서비스 추상화 기술과 환경에 종속되는 트랜잭션 경계설정 코드 현재까지 코드의 문제점
JDBC 활용 기술에 국한됨 다른 DB 접근 방식 (JPA, 하이버네이트)에는 사용 못함 로컬 트랜잭션 (Local Transaction) 하나의 DB Connection 안에 경계가 설정되는 트랜잭션

23 5.2.4 트랜잭션 서비스 추상화 기술과 환경에 종속되는 트랜잭션 경계설정 코드
UserService에 트랜잭션 경계설정 코드를 도입한 이후의 클래스간의 의존관계 트랜잭션 처리를 위하여 UserDao의 구체적인 클래인 UserDaoJdbc에 의존적으로 되어 버림 UserDaoHibernate를 지원하기 위해서는 별도의 또 다른 UserService 코드가 필요함

24 5.2.4 트랜잭션 서비스 추상화 트랜잭션 API의 의존관계 문제와 해결책 스프링의 접근법: 트랜잭션 처리 코드의 추상화
트랜잭션 경계설정을 담당하는 코드는 일정한 패턴을 갖는 구조 JDBC, JTA, JPA, 하이버네이트, JDO, JMS에 있는 모든 트랜잭션 경계설정 코드에 대한 공통적인 특징을 추출하여 추상화된 트랜잭션 관리 계층 설정 스프링의 o.s.transaction.PlatformTransactionManager 인터페이스

25 5.2.4 트랜잭션 서비스 추상화 스프링의 PlatformTransactionManager 활용 코드
o.s.jdbc.datasource.DataSourceTransactionManager JdbcTemplate에서 사용되는 방식(JDBC 방식)으로 트랜잭션 관리

26 5.2.4 트랜잭션 서비스 추상화 트랜잭션 기술 설정의 분리 각 기술별 글로벌 트랜잭션 지원 코드
JTA PlatformTransactionManager txManager = new JTATransationManager(…) 하이버네이트 PlatformTransactionManager txManager = new HibernateTransactionManager(…) JPA PlatformTransactionManager txManager = new JPATransactionManager(…) 즉, 각 기술별로 위 한줄만 변경하면 되며 나머지 코드는 모두 동일 그렇다면 스프링의 빈 등록 및 DI 기능 사용과 정확히 맞아떨어짐 스프링이 제공하는 모든 PlatformTransactionManager는 멀티 쓰레드에 안전하게 구현되어 있음  싱글톤 빈 등록 가능

27 5.2.4 트랜잭션 서비스 추상화 트랜잭션 메니저를 빈으로 분리시킨 UserService

28 5.2.4 트랜잭션 서비스 추상화 트랜잭션 메니저의 빈 등록 및 DI
만약 JDBC가 아닌 JTA를 이용하는 것으로 수정하고 싶다면 빈 등록을 다음과 같은 코드로 수정

29 5.2.4 트랜잭션 서비스 추상화 테스트 코드에서 트랜잭션 메니저의 수동 DI

30 5.3 서비스 추상화와 단일 책임 원칙

31 5.3.1 수직, 수평 계층구조와 의존관계 스프링에서 인터페이스를 활용한 추상화 기법 동일한 계층에서의 수평적 분리
각 모듈 간에 불필요한 영향을 주지 않으면서 독자적으로 확장 가능하게 해줌 개방 폐쇄 원칙(OCP, Open-Closed Principle, 1장 참고)을 지키도록 도와줌 스프링의 DI가 매우 중요한 역할을 담당 동일한 계층에서의 수평적 분리 UserService와 UserDao의 분리 TransactionManager와 DataSource의 분리 서로 다른 계층에서의 수직적 분리 UserService와 PlatformTransactionManager의 분리 UserDao와 DataSource의 분리

32 5.3.1 수직, 수평 계층구조와 의존관계 계층과 책임의 분리 수직적 분리에 따르는 장점
DB 연결 기술이 변경되어도 UserDao는 전혀 영향이 없음 DB가 변경되고 트랜잭션 관리 방법이 변경되어도 UserService에는 전혀 영향이 없음 즉, 애플리케이션 코드를 로우레벨의 기술 서비스 및 환경과 독립시켜준다.

33 5.3.2 단일 책임 원칙 단일 책임 원칙 (Single Responsibility Principle)
하나의 모듈은 한 가지 책임을 가져야 함을 의미 두 가지 책임을 가진다는 것은 코드가 수정되어야 할 이유가 두 가지라는 것 UserService가 레벨 관리 및 JDBC Connection 연결을 담당할 경우 사용자 관리 로직 변경시에 코드 수정 필요 JDBC에서 JTA로 트랜잭션 관리 변경시에도 코드 수정 필요 단일 책임 원칙을 지킬 때의 장점 변경이 필요한 수정 대상이 명확해짐

34 5.3.2 단일 책임 원칙 핵심적인 스프링 원칙 정리 위 원칙을 지원하는 스프링의 가장 중요한 기능
추상화 기법 도입 (적절하게 책임과 관심이 다른 코드 분리) 단일 책임 원칙 개발 폐쇄 원칙 모듈간 결합도 낮아짐 응집도 높아짐 전략 패턴, 어댑터 패턴 등, 다양한 디자인 패턴이 자연스럽게 적용 싱글톤 레지스트리 덕분에 자동화된 테스트 구축 쉬움 위 원칙을 지원하는 스프링의 가장 중요한 기능 DI (Dependency Injection) 전통적인 IoC (Inversion of Control) 디자인 패턴에 대한 스프링에서의 디자인 패턴 이름


Download ppt "5.2 트랜잭션 서비스 추상화."

Similar presentations


Ads by Google