5.2 트랜잭션 서비스 추상화
- 테스트 실패의 원인
트랜젝션의 문제, upgradeLevels() 메서드가 하나의 트랜잭션 안에서 동작하지 않았기 때문이다.
5.2.2 트랜잭션 경계설정
트랜잭션 롤백 - 두 개의 SQL 작업 중 하나가 실패하면 앞에서 처리한 SQL 작업도 취소 시키는 것
- JDBC 트랜잭션의 트랜잭션 경계설정
트랜잭션의 경계 - 애플리케이션 내에서 트랜잭션이 시작되고 끝나는 위치를 트랜잭션의 경계라고 부른다.
로컬 트랜잭션 - 하나의 DB 커넥션 안에서 만들어지는 트랜잭션을 로컬 트랜잭션이라고 한다.
- UserService와 UserDao의 트랜잭션 문제
왜 UserService의 upgradeLevels()에는 트랜잭션이 적용되지 않았을까?
JdbcTemplate에서는 메소드 호출 한 번에 한 개의 DB 커넥션이 만들어지고 닫히는 일까지 일어난다. 따라서 총 3번의 트랜잭션이 일어나고 결과는 롤백이 발생하더라도 DB에 남는다.
- 비즈니스 로직 내의 트랜잭션 경계설정
UserService에서 connection을 공유 받아서 같은 connection 내의 트랜잭션을 발생 시키자
- UserService 트랜잭션 경계설정의 문제점
위 방법은 트랜잭션 문제는 해결할 수 있겠지만 여러 가지 새로운 문제가 발생한다.
1. JdbcTemplate을 더 이상 활용할 수 없다는 점
2. DAO의 메소드와 비즈니스 로직을 담고 있는 UserService의 메소드에 Connection 파라미터가 추가돼야 한다는 점
3. UserDao는 더 이상 데이터 액세스 기술에 독립적일 수가 없다는 점
4. Connection 파라미터를 통해 테스트 코드에도 영향을 미친다.
5.2.3 트랜잭션 동기화
- Connection 파라미터 제거
트랜잭션 동기화 - UserService에서 트랜잭션을 시작하기 위해 만든 Connection 오브젝트를 특별한 저장소에 보관해두고, 이후에 호출되는 DAO의 메소드에서는 저장된 Connection을 가져다가 사용하게 하는 것
- 트랜잭션 동기화 적용
스프링은 트랜잭션 동기화 기능을 지원하는 간단한 유틸리티 메소드를 제공한다.
- 트랜잭션 테스트 보완
- JdbcTemplate과 트랜잭션 동기화
5.2.4 트랜잭션 서비스 추상화
- 기술과 환경에 종속되는 트랜잭션 경계설정 코드
글로벌 트랜잭션 - 별도의 트랜잭션 관리자를 통해 트랜잭션을 관리
- 트랜잭션 API의 의존관계 문제와 해결책
트랜잭션 추상계층이 제공하는 API를 이용하자.
- 스프링의 트랜잭션 서비스 추상화
- 트랜잭션 기술 설정의 분리
5.3 서비스추상화와 단일 책임 원칙
- 수직, 수평 계층구조와 의존관계
- 단일 책임 원칙
하나의 모듈은 한 가지 책임을 가져야 한다는 의미이다.
- 단일 책임 원칙의 장점
어떤 변경이 필요할 때 수정 대상이 명확해진다.
기술이 바뀌면 기술계층과의 연동을 담당하는 기술 추상화 계층의 설정만 바꿔주면 된다.
5.4 메일 서비스 추상화
5.4.1 JavaMail을 이용한 메일 발송 기능
- JavaMail 메일 발송
5.4.2 JavaMail이 포함된 코드의 테스트
운영중인 메일 서버를 테스트 하기에는 부하가 생길 위험이 있다. 테스트용 메일 서버를 따로 만들어서 테스트를 해 주자.
5.4.3 테스트를 위한 서비스 추상화
JavaMail대신에 테스트에서 사용할, JavaMail과 같은 인터페이스를 갖는 오브젝트를 만들어서 사용하면 문제는 모두 해결된다.
- JavaMail을 이용한 테스트의 문제점
JavaMail의 API는 이 방법을 적용할 수 없다는 문제점이 있다.
스프링은 JavaMail을 사용해 만든 코드의 테스트를 위해 JavaMail에 대한 추상화 기능을 제공하고 있다.
- 메일 발송 기능 추상화
package org.springframework.mail;
...
public interface MailSender {
void send(SimpleMailMessage simpleMessage) throws MailException;
void send(SimpleMailMessage[] simpleMessages) throws MailException;
}
- 테스트용 메일 발송 오브젝트
아무것도 하지 않는 MailSender 구현 빈 클래스를 만들어보자.
- 테스트와 서비스 추상화
5.4.4 테스트 대역
- 의존 오브젝트의 변경을 통한 테스트 방법
- 테스트 대역의 종류와 특징
테스트 스텁 = 테스트 대상 오브젝트의 의존객체로서 존재하면서 테스트 동안에 코드가 정상적으로 수행할 수 있도록 돕는 것을 말한다.
- 목 오브젝트를 이용한 테스트
6장. AOP
AOP는 IoC/DI, 서비스 추상화와 더불어 스프링의 3대 기반기술의 하나이다.
6.1 트랜잭션 코드의 분리
6.1.1 메소드 분리
비즈니스 로직을 담당하는 코드를 메소드로 추출해서 독립시켜보자.
6.1.2 DI를 이용한 클래스의 분리
트랜잭션 코드를 클래스 밖으로 뽑아내자.
- DI 적용을 이용한 트랜잭션 분리
- 분리된 트랜잭션 기능
- 트랜잭션 분리에 따른 테스트 수정
- 트랜잭션 경계설정 코드 분리의 장점
1. 비즈니스 로직을 담당하고 있는 UserServiceImpl의 코드를 작성할 때는 트랜잭션과 같은 기술적인 내용에는 전혀 신경 쓰지 않아도 된다.
2. 비즈니스 로직에 대한 테스트를 손쉽게 만들어낼 수 있다.
6.2 고립된 단위 테스트
테스트는 작은 단위로 하면 좋다. 그러나 테스트 대상이 다른 오브젝트와 환경에 의존하고 있다면 작은 단위의 테스트가 주는 장점을 얻기 힘들다.
6.2.1 복잡한 의존관계 속의 테스트
의존관계를 따라 등장하는 오브젝트와 서비스, 환경 등이 모두 합쳐져 테스트 대상이 된다.
6.2.2 테스트 대상 오브젝트 고립시키기
- 테스트를 위한 UserServiceImpl 고립
- 고립된 단위 테스트 활용
- UserDao 목 오브젝트
- 테스트 수행 성능의 향상
6.2.3 단위 테스트와 통합 테스트
단위 테스트 - 테스트 대상 클래스를 목 오브젝트 등의 테스트 대역을 이용해 의존 오브젝트나 외부의 리소스를 사용하지 않도록 고립시켜서 테스트 하는 것
통합 테스트 - 외부의 DB나 파일, 서비스 등의 리소스가 참여하는 테스트
6.2.4 목 프레임워크
- Mockito 프레임워크
목 클래스를 일일이 준비해둘 필요가 없다. 간단한 메소드 호출만으로 다이내믹하게 특정 인터페이스를 구현한 테스트용 목 오브젝트를 만들 수 있다.
6.3 다이내믹 프록시와 팩토리 빈
6.3.1 프록시와 프록시 패턴, 데코레이터 패턴
프록시 - 클라이언트의 요청을 받아주는 것을 대리자, 대리인과 같은 역할을 한다고 해서 프록시라고 부른다.
타겟 - 프록시를 통해 최종적으로 요청을 위임받아 처리하는 실제 오브젝트
클라이언트 -> 프록시 -> 타깃
- 데코레이터 패턴
타깃에 부가적인 기능을 런타임 시 다이내믹하게 부여해주기 위해 프록시를 사용하는 패턴
ex) InputStream, OutputStream
프록시 패턴
타깃에 대한 접근 방법을 제어하려는 목적
6.3.2 다이내믹 프록시
- 프록시의 구성과 프록시 작성의 문제점
1. 타깃의 인터페이스를 구현하고 위임하는 코드를 작성하기가 번거롭다.
2. 부가기능 코드가 중복될 가능성이 많다.
JDK의 다이내믹 프록시를 이용해서 해결하자.
- 리플렉션
- 프록시 클래스
- 다이내믹 프록시 적용
- 다이내믹 프록시의 확장
6.3.3 다이내믹 프록시를 이용한 트랜잭션 부가기능
- 트랜잭션 InvocationHandler
- TransactionHandler와 다이내믹 프록시를 이용하는 테스트
6.3.4 다이내믹 프록시를 위한 팩토리 빈
- 팩토리 빈
static 메소드를 사용하자.
'Spring-Boot' 카테고리의 다른 글
토비의 스프링 3.1 Vol.1 550p ~ 650p 정리 (0) | 2024.01.02 |
---|---|
토비의 스프링 3.1 Vol.1 450p ~ 550p 정리 (0) | 2024.01.02 |
토비의 스프링 3.1 Vol.1 250p ~ 350p 정리 (1) | 2023.12.30 |
토비의 스프링 3.1 Vol.1 145p ~ 250p 정리 (1) | 2023.12.28 |
토비의 스프링 3.1 Vol.1 40p ~ 143p 정리 (1) | 2023.12.28 |