6. AOP
기존의 강한 의존관계
트랜잭션 오브젝트가 적용된 의존관계
기존 의존구조
테스트 대상 오브젝트를 고립시킨 의존구조
테스트 대상이 환경이나 외부 서버, 다른 클래스의 코드에 종속되고 영향을 받지 않도록 고립
단위테스트/통합테스트 가이드라인
- 항상 단위테스트를 먼저 고려한다.
- 하나의 클래스나 성격과 목적이 같은 긴밀한 클래스 몇 개를 모아서 외부와의 의존관계를 모두 차단하고 필요에 따라 스텁이나 목 오브젝트 등의 테스트 대역을 이용하도록 테스트를 만든다.
- 외부 리소스를 사용해야만 가능한 테스트는 통합 테스트로 만든다.
- DAO 테스트는 DB(외부 리소스)를 사용해야만 하므로 통합테스트. DAO를 이용하는 코드는 DAO 역할을 스텁이나 목 오브젝트로 대체해서 테스트한다.
- 스프링 테스트 컨텍스트 프레임워크를 이용하는 테스트는 통합 테스트다.
Mockito
목 프레임워크
다이내믹 프록시와 팩토리 빈
클라이언트는 인터페이스만 보고 사용하기 때문에 본인은 핵심기능을 가진 클래스를 사용하리라 기대하지만, 실제로는 부가기능을 통해 핵심기능을 이용.
프록시
클라이언트가 사용하려고 하는 실제 대상인 것처럼 위장해서 클라이언트의 요청을 받아주는 오브젝트
타깃과 같은 인터페이스를 구현하고 있으며 타깃을 제어할 수 있는 위치에 존재
프록시 기능
타깃(실체)
프록시를 통해 최종적으로 요청을 위임받아 처리하는 실제 오브젝트
데코레이터 패턴
타깃에 부가적인 기능을 런타임 시에 다이나믹하게 부여해주기 위해 프록시를 사용하는 패턴
인터페이스를 통해 위임하는 방식
단점
- 프록시를 적용할 대상이 구현하고 있는 인터페이스를 구현하는 프록시 클래스를 일일이 만들어야 한다는 번거로움
- 부가적인 기능이 여러 메소드에 반복적으로 나타나게 돼서 코드 중복의 문제 발생
프록시 패턴
프록시 클라이언트와 타깃 사이에서 대리 역할을 맡은 오브젝트를 두는 방법의 총칭
프록시 패턴 프록시를 사용하는 방법 중에서 타깃에 대한 접근 방법을 제어하려는 목적을 가진 경우
프록시는 코드에서 자신이 만들거나 접근할 타깃 클래스 정보를 알고 있는 경우가 많다.
다이내믹 프록시
일일이 프록시 클래스를 정의하지 않고도 몇가지 API를 이용해 프록시처럼 동작하는 오브젝트를 다이내믹하게 생성
프록시 팩토리에 의해 런타임 시 다이내믹하게 만들어지는 오브젝트
프록시를 만들기가 번거로운 이유
- 타깃의 인터페이스를 구현하고 위임하는 코드를 작성하기가 번거롭다.
- 부가기능 코드가 중복될 가능성이 많다는 점.
리플렉션
자바 코드 자체를 추상화해서 접근하도록 만든 것.
invoke()
다이내믹 프록시의 동작방식
프록시 팩토리에게 인터페이스 정보만 제공해주면 해당 인터페이스를 구현한 클래스의 오브젝트를 자동으로 생성한다.
프록시로서 필요한 부가기능 코드만 따로 넣어준다.
부가기능을 가진 InvocationHandler 예제
InvocationHandler를 구현하면 다이내믹 프록시로부터 메소드 호출 정보를 받아서 처리
다이내믹 프록시가 클라이언트로부터 받는 모든 요청은 invoke()로 전달
다이내믹 프록시를 위한 팩토리 빈
DI의 대상이 되는 다이내믹 프록시 오브젝트는 일반적인 스프링의 빈으로 등록할 방식이 없다. 클래스도 모르기 때문.
팩토리 빈
스프링을 대신해서 오브젝트의 생성로직을 담당하도록 만들어진 특별한 빈
스프링의 FactoryBean 인터페이스를 구현
프록시 팩토리 빈 방식의 장점과 한계
- 장점
- 한 번 부가기능을 가진 프록시를 생성하는 팩토리 빈을 만들어두면 타깃의 타입에 상관없이 재사용할 수 있다.
- 프록시 빈을 빠르고 효과적으로 적용 가능
- 데코레이터 패턴의 단점 보완
- 한계
- 한 번에 여러 개의 클래스에 공통적인 부가기능을 제공하는 일이 불가능
- 하나의 타깃에 여러 개의 부가기능을 적용하려고 할 때도 문제
- 부가기능 제공 클래스가 프록시 팩토리 빈 개수만큼 생성
스프링의 프록시 팩토리 빈
순수하게 프록시를 생성하는 작업만 담당하며 제공할 부가기능은 별도의 빈에 둘 수 있다.
InvocationHandler.invoke() 타깃 오브젝트에 대한 정보를 제공하지 않는다.
MethodInterceptor.invoke() 타깃 오브젝트에 대한 정보를 제공받는다.
→ 타깃 오브젝트에 상관없이 독립적으로 만들어지므로 타깃이 다른 여러 프록시에서 공유할 수 있고, 싱글톤 빈으로 등록이 가능하다.
어드바이스Advice
타깃 오브젝트에 적용하는 부가기능을 담은 오브젝트
타깃 오브젝트에 종속X
포인트컷
메소드 선정 알고리즘을 담은 오브젝트
프록시의 핵심 가치는 타깃을 대신해서 클라이언트의 요청을 받아 처리하는 오브젝트로서의 존제 자체이므로 대상 선정 알고리즘을 프록시로부터 분리
스프링 AOP
자동 프록시 생성
부가기능 적용이 필요한 타깃 오브젝트마다 거의 비슷한 내용의 ProxyFactoryBean 설정 정보를 추가해 주지 않아도 된다.
빈 후처리기를 이용한 자동 프록시 생성
DefaultAdvisorAutoProxyCreator를 상속했으면 등록한 빈을 전부 읽어들이고 요청에 따라 매핑된 친구 찾아서 자동 프록시 생성
- 스프링은 빈 오브젝트를 만들 때마다 후처리기에게 빈을 전송
- 후처리기는 등록된 모든 어드바이저 내의 포인트컷을 이용해 전달받은 빈이 프록시 적용 대상인지 확인
- 포인트컷 동작 방식2) 메소드 매처를 이용해 어드바이스를 적용할 메소드인지 판단
- 1) 클래스 필터를 이용해 프록시 적용 대상 클래스인지 판단
- 프록시 적용 대상이면, 내장된 프록시 생성기에게 현재 빈에 대한 프록시를 만들게 하고, 만들어진 프록시에 어드바이저 연결
- 프록시 생성되면 원래 컨테이너가 전달해 준 빈 오브젝트 대신 프록시 오브젝트를 컨테이너에게 반환
- 컨테이너는 빈 후처리기가 돌려 준 오브젝트를 빈으로 등록 후 사용
- → 프록시 방식을 차용한 스프링의 AOP는 싱글톤일 수 없음
포인트컷 표현식
클래스 패턴이 아닌 타입 패턴으로 부른다. 인터페이스와 구현 클래스를 동일한 타입으로 간주.
AOP 적용기술
스프링에서의 AOP 프록시 이용
바이트코드를 이용한 AOP AspectJ
용어 정리
AOP에 사용되는 빈
- 자동 프록시 생성기 (빈 후처리기)
- 어드바이스
- 포인트컷
- 어드바이저
→ aop autoproxy를 xml로 등록하고 @Aspect 등의 어노테이션으로 처리
트랜잭션
더 이상 쪼갤 수 없는 최소 단위의 작업
경계 안에서 진행된 작업은 commit()을 통해 전부 반영하든지 rollback()을 통해 전부 취소해야 함
선언적 트랜잭션declarative transaction
AOP를 이용해 코드 외부에서 트랜잭션의 기능을 부여해주고 속성을 지정할 수 있게 하는 방법
프로그램에 의한 트랜잭션programmatic transaction
TransactionTemplate이나 개별 데이터 기술의 트랜잭션 API를 사용해 직접 코드 안에서 사용하는 방법
- 트랜잭션 전파Transaction propagation
디폴트. 진행 중인 트랜잭션이 없으면 새로 시작하고, 이미 시작된 트랜잭션이 있으면 참여항상 새로운 트랜잭션을 시작한다. 독립적인 트랜잭션이 보장되어야 하는 코드에 적용트랜잭션 없이 동작한다. 대다수의 메소드에 트랜잭션을 적용해야 할 경우, 전체에 트랜잭션을 적용하고 해당 속성을 통해 일부 메소드만 트랜잭션을 해제
- 3) PROPAGATION_NOT_SUPPORTED
- 2) PROPAGATION_REQUIRED_NEW
- 1) PROPAGATION_REQUIRED
- 트랜잭션의 경계에서 이미 진행 중인 트랜잭션이 있거나 없을 때 어떻게 동작할 것인지 결정하는 방식
- 격리수준Isolation level
- 모든 DB 트랜잭션은 격리수준을 가져야 한다.
- 제한시간
- default none
- 읽기전용read only
- default readonly=false
포인트컷 전략
- 트랜잭션 포인트컷 표현식은 타입 패턴이나 빈 이름을 이용
- 공통된 메소드 이름 규칙을 통해 최소한의 트랜잭션 어드바이스와 속성 정의
- 프록시 방식 AOP는 같은 타깃 오브젝트 내의 메소드를 호출할 때는 미적용
- 스프링 API를 이용해 프록시 오브젝트에 대한 ref를 가져온 후 같은 오브젝트의 메소드 호출도 프록시를 이용하도록 강제하는 방법
- AspectJ처럼 바이트코드를 직접 조작하는 AOP 적용하기
- 해결방법
트랜잭션 속성 전략
- 특정 계층의 경계를 트랜잭션 경계와 일치시킨다.
@Transaction
메소드 단위로 적용
4단계의 대체fallback정책을 이용할 수 있다.
메소드의 속성을 확인할 때
타깃메소드→타깃클래스→선언메소드→선언타입(클래스, 인터페이스)
순서로 @Transaction 적용됐는지 확인하면서 가장 먼저 발견되는 속성 정보 사용
테스트에 붙은 @Transaction은 @Rollback(true)라서 실행 후 자동롤백된다.
스프링에서 트랜잭션을 적용할 때
- 트랜잭션 경계가 DAO외부에 있고 범위가 넓은 경우 → AOP 이용
- SQL 레지스트리 같은 제한된 오브젝트 내에서 서비스에 특화된 간단한 트랜잭션이 필요한 경우 → 간단하게 트랜잭션 추상화 API이용
'Web > Spring' 카테고리의 다른 글
[Custom datasource] hikary.jdbc-url 쓰다가 Failed to configure a DataSource: 'url' 발생 (0) | 2021.11.06 |
---|---|
[Swagger] Swagger2 2.xx 버전오류 (0) | 2021.11.02 |
[Spring] OXM, 서비스 추상화, 빈 애노테이션, @Import, @Profile, @Enable* (0) | 2021.10.29 |
[Spring] 테스트, 템플릿, 예외 (0) | 2021.10.29 |
[Spring] IoC, Singleton, DI, 팩토리메소드/템플릿메소드/전략 패턴 (0) | 2021.10.29 |
댓글