IoC/DI/AOP
스프링에 가장 인기있는 AOP 적용대상은 선언적 태른 잭션 기능
트랜잭션 경계설저 기능을 AOP를 이용해 바꿔보자
트랜 잭션 경계설정의 코드와 비즈니스 로직 코드간 구분되어 있음
주고 받는 정보도 없음
분리
트랜잭션 코드를 클래스 밖으로 뽑아내자
간접적으로 사용 해보자 .
테스트와 실제 사용하는 클래스를 선택해서 적용하고싶어서
DI 적용해서 구현클래스를 만들어준다.
근대 꼭 한가지 클래스만 선택해서 사용할 필요가없다.
먼저 기존의 UserService -> UserServiceImpl로 변경
UserService 인터페이스 생성
impl 트랜잭션 코드를 다삭제해주고..
변경후 -> 로직만 구현한 형태가 됨,
서버환경? 스프링 관련코드? 단순히 UserDao라는 인터페이스를 이용하고
User라는 도메인 정보를 가진 비즈니스 로직에만 충실한 깔끔한 코드
UserServiceTx 만들자.
Tx 는 단순히 UserService 구현 오브젝트 기능을 위임
경계설정 작업 부여해야됨.
이렇게함으로써 기존에 비즈니스로직 분리 트랜잭션 경계설정 분리 코드
모습이 나타났다
DI를 두개 적용 받아서 코드처리를했다. 음.. 이런모습이 맞는걸까
스프링 기능을 잘 사용하고 있다고하는데..
밑에서 계속 보자.
트랜잭션 담당 오브젝트가 트랜잭션 작업을 해주고
실제 사용자 관리 로직 호출 !
DI를 적용해 클래스 들이 분리 됬으니 이렇게 적용해주자
이런식으로 테스트 를 변경해줌 .
가장 좋은 테스트 방법은 가능한 작은 단위로 쪼개서 테스트 하는 것.
테스트 단위가 작아야 원인을 찾고 내용도 분명해짐
하지만 작은 단위로 테스트를 하고 싶다해도 없는 경우가 많다.
테스트 대상이 다른 오브젝트와 환경에 의존하고 있다면
작은 단위 테스트가 주는 장점을 얻기 힘들다..
UserSErvice. 는 UserDao, TransactionManager,MailSender 라는 세 가지의 의존관계를 가지고 있음..
위 3가지는 또 밑으로 다른 구현 클래스들이 줄줄이 엮여있어서 다 같이
실행 된다..
MailSender에서 적용해봤떤 ㅌ대로 테스트를 위한 대역을 만들어야됨..
Ex)DummymailSender 라는 테스트 스텁
MockMailSender 목오브젝트
UserDao 는 단지 테스트 대상 코드가 정상적으로 수행되도록 도와주는 스텁이 아니라 검증 기능까지 가진 목 오브젝트
고립된 환경에서 upgradeLevels() 테스트 결과를 검증할 방법이 필요하기 때문
DB에 의존하고 있는 테스트 목오브젝트를 적용시켜보자
update()에 대해서는 목 오브젝트로서 동작하는 UserDao 타입의 테스트 대역이 필요함.
MockUserDao
두 개의 User 타입 리스트 정의
하나는 생성자를 통해 전달받는 사용자 목록,
다른 하나는 update() 메소드를 실행하면서 넘겨주는 업그레이드 대상
UserServiceTest의 upgradeLevels() 실행
성능이 훨씬 빨라짐
외부 리소스를 사용하지않고 테스트 하는걸 단위테스트로 지정 고립된상태..
반대는 통합테스트..
항상 단위 테스트 먼저 고려한다.
하나의 클래스나 성격과 목적이 같은 클래스를 모아 목오브젝트 등을 이용해 테스트 만든다.
외부 리스소를 사용해야만 가능하면 통합테스트로.
단위테스트로 만들기 어려운경우 ? EX) DAO DB까지 연동되는 테스트로
만드는 편이 좋다.
DAO 테스트는 DB 외부 리소스를 사용하기 때문에 통합 테스트로 분류됨. 하지만
코드 기준으론 ㅌ단위 테스트.
단위테스트를 충분히 거치면 통합테스트 위험은 많이 줄어듬..
단위 테스트가 만들기 너무 복잡? 처음 부터 통합테스트로 고려해본다.
가능한 많이 단위테스트 만들고..
스프링 테스트 컨텍스트 프레임워크를 이용하는 테스트는 통합 테스트.
테스트 마다 목 프레임워크를 제작해주면 번거롭다..
그래서...?
간단한 메소드 호출만으로 다이내믹하게 특정 인터페이스를 구현한 테스트용 목 오브젝트를 만듬.
아직 아무런 기능이 없는 목 오브젝트.
사용자 목록을 불러오도록 스텁 기능을 추가해보자
Mockito 목 오브젝트는 다음의 네 단계를 거쳐서 사용됨. 두 번째와 네 번째는 필요한 경우만.
트랜잭션 경계코드 설정 을 다시설명.. 부가기능이 핵심기능을 사용하는 것처럼..
부가기능을 통해 핵심기능을 이용하게된다..
이렇게 마치 자신이 클라이언트가 사용하려고 하는 실제 대상인 것처럼 위장해서
클라이언트의 요청을 받아주는 것을 대리자 대리인 역활이라해서 프록시라 부름
프록시를 통해 최종적으로 위임 받아 처리하는 오브젝트를 타깃/실체 라고 함
프록시 사용이유.
데코레이션 패턴이란? 부가적인 기능을 런타임시 다이내믹하게 보여해주기 위해 프록시를
사용하는 패턴.
동적으로 기능을 부가한다?
컴파일 시점 , 즉 코드상에서 어떤 방법과 순서로 프록시와 타깃이 연결되어 사용되는지
정해지지 않다는 뜻.
데코레이터라고 불리는 이유? 케잌의 장식 붙이는 것처럼 실제 내용은 동일하지만
부가적인 효과를 누릴수 있기 때문에
프록시를 여러개 사용함.
UserServiceTx 데코레이터 패턴 적용
여기서 말하는 프록시는 사용하는 방법 중에서 타깃에 대한 접근 방법을 제어하는 목적
프록시 패턴의 프록시는 타깃의 기능을 확장하거나 추가하지 않음.
대신 타깃에 접근 방식 변경.
타깃 오브젝트에 레퍼런스가 미리 필요할 경우 프록시 패턴을 적용하면 됨..
Collection 의 unmodifiableColletion() 전형적인 접근권한 제어 프록시
프록시는 기존 코드에 영향을 주지 않으면서 타깃의 기능을 확장하거나 접근 방법 제어함.
하지만 프록시 만드는일은 매우 번거로움
프록시의 기능
프록시를 만드는 번거로운 이유?
다이내믹 프록시는 리플렉션 기능을 이용해서 프록시를 만들어 줌.
리플렉션은 자바의 코드 자체를 추상화해서 접근하도록 만듬
리플렉션 API 중 Method라는 인터페이스를 이용해 메소드 호출 방법을 살펴보자
스트링이 가진 메소드 중에서 Length라는 이름을 갖고 있고 파라미터는 없는 메소드 정보를 가져온다.
다이내믹 프록시를 이용한 프록시 만들기.
다이내믹 프록시는 프록시 팩토리에 의해 런타임 시 다이내믹하게 만들어지는 오브젝트.
다이내믹 프록시는 인터페이스 구현 클래스의 오브젝트를 만들어 주지만. 프록시로서 필요한 부가기능 제공 코드는
직접 작성해야 함..
리턴 타입이 스트링이아닌경우 ?
InovacationHandler 방식의 또한가지 장점은 타깃의 종류와 상관없이도 적용이 가능함.
어떤 종류의 인터페이스를 구현한 타깃이든 항관없이 재사용할 수 있고 메소드 리턴 타입이 스트링 경우만
대문자로 결과로 바꿔주도록 Uppercasehandler 만들수 있다.
트랜잭션 부가기능을 제공하는 다이내믹 프록시를 만들어 적용하는 방법이 효율적..
일반적인 스프링 빈으로 다이내믹 프록시 오브젝트는 등록할 방법이 없음
다이내믹 프록시는 Proxy 클래스의 newProxyInstance() 라는 스태틱 팩토리 메소드를 통해서만 만듬
팩토리 빈을 이용한 빈생성 방법.
스프링을 대신해 오브젝트의 생성로직 담당
프록시부분 점프 .. 음 다시한번 되새김질 할때 봐야겠따 전체적으로 내용을 이해하기어렵고.
너무 시간을 잡아먹는다. 중요한 부분이라면 다시볼 기회가 있을때 더공부하자
반복적으로 등장하던 태랜잭션 코드를 깔끔하고 효과적으로 분리.
투명한 부가기능도 제공돼야 한다.
투명하다? 기존 설계 코드에 영향 X
ㅇ