6장 AOP(2)

Soonwoo Kwon·2022년 3월 28일
0

토비의 스프링

목록 보기
7/11

6.5 스프링 AOP

자동 프록시 생성

중복 문제의 접근

타깃 오브젝트로의 위임 코드와 부가기능을 제공하기 위한 코드가 프록시가 구현해야 하는 모든 인터페이스 메소드마다 반복적으로 필요했다. 이 문제를 해결하기 위해 다이내믹 프록시를 사용하였다. 타깃의로의 위임과 부가기능 적용 여부 판단은 다이내믹 프록시가 담당하고, DI를 통해 제공하였다.

빈 후처리기를 이용한 자동 프록시 생성기

BeanPostProcessor이라는 인터페이스를 구현하여 빈 후처리기를 생성할 수 있고, 책에서는DefalutAdvisorAutoProxyCreator을 이용한다. 빈 후처리기를 빈으로 등록하여 사용할 수 있고, 빈 후처리기를 통해 빈 오브젝트를 수정하고 별도의 작업을 수행할 수 있다.

빈 후처리기를 자동 프록시 생성에 적용해보자.

  • DefalutAdvisorAutoProxyCreator가 빈으로 등록되어 있으면 빈 오브젝트가 생성될 때 마다 후처리인DefalutAdvisorAutoProxyCreator에게 빈을 보낸다.
  • 어드바이저의 포인트컷을 이용해 해당 빈이 프록시 적용 대상인지 확인한다.
  • 적용 대상이라면 내장된 프록시 생성기에 현재 빈에 대한 프록시를 생성하게 하고, 생성된 프록시에 어드바이저를 연결해준다.
  • 빈 후처리기는 전달받은 빈 오브젝트 대신 프록시 오브젝트를 컨테이너에 돌려준다.
  • 컨테이너는 빈 후처리기가 돌려준 오브젝트를 ㅅ 빈으로 등록하고 사용한다.

확장된 포인트컷

기존 포인트컷은 어떤 메소드에 부가기능을 적용할지 선정하는 역할로 사용되었다. 빈 후처리기가 자동 프록시 생성을 하는 과정에서는 어떤 빈에 프록시를 적용할지를 선정하는 역할을 한다. Pointcut 인터페이스는 다음과 같이 정의된다.

public interface Pointcut{
	ClassFilter getClassFilter();
    MethodFileter getMethodMatcher();
}

빈 후처리기는 getClassFilter() 메소드를 통해 클래스 레벨에서 프록시를 적용할 클래스인지 판단할 수 있다. 그 이후 getMethodMatcher() 메소드를 통해 어드바이스를 적용할 메소드인지 확인한다. 만약 클래스 레벨에서 프록시를 적용할 메소드로 선정되지 않는다면 부가 기능 역시 적용될 가능성이 없다.

포인트컷 등록

포인트컷을 빈에 등록하기 위해 클래스 이름 패턴과 메소드 이름 패턴을 각각 지정하여 등록한다. 테스트시에 지정된 클래스만 프록시가 적용되는지 확인하고 싶다면 이 이름 패턴을 수정하며 간단하게 확인해 볼 수 있다.

포인트컷 표현식을 이용한 포인트컷

지금까지는 포인트컷을 메소드의 이름 패턴과 클래스 이름 패턴을 비교하는 단순한 방법을 사용할 수 있다. 리플렉션 API를 통해 더 복잡하고 상세한 클래스와 메소드의 메타정보를 얻어올 수 있고, 이를 포인트컷에 적용할 수 있다. 하지만 리플렉션 API 코드를 작성하는 것은 번거로운 일이고, 비교 조건이 변경되었을 번거롭게 수정해야 한다. 스프링은 이보다 더 효과적으로 포인트컷의 클래스와 메소드를 선정하는 알고리즘을 작성할 수 있는 방법을 제공한다.

포인트컷 표현식 문법

execution([접근제한자 패턴] 타입패턴 [타입패턴.]이름패턴 (타입패턴 | "..", ...)[thows 예외 패턴])

[]로 감싸진 부분은 옵션 항목이고, 각 이름 패턴에 대해 와일드카드('*','..') 형식을 적용할 수 있다.

타입 패턴과 클래스 이름 패턴

포인트컷 표현식에서 클래스 이름에 적용되는 것은 해당 클래스의 이름 패턴이 아니라 타입 패턴이다.

AOP란 무엇인가?

UserLevelUp 기능을 구현하기 위해 트랜잭션 경계 설정 코드가 필요하였고 해당 코드로 부터 비즈니스 로직을 구현한 코드가 분리되기 위해 다음과 같은 시도가 있었다.

  • 트랜잭션 서비스 추상화
  • 프록시와 데코레이터 패턴
  • 다이내믹 프록시와 프록시 빈
  • 자동 프록시 생성 방법과 포인트컷
  • 부가기능의 모듈화

AOP: 애스펙트 지향 프로그래밍

트랜잭션 경계설정과 같은 부가기능은 핵심기능과 같은 방법으로 모듈화하기 어렵다. 이러한 부가기능을 모듈화할지 연구해온 사람들이 Aspect라는 개념을 도입하였다. Aspect는 애플리케이션의 핵심기능을 담고 있지 않지만, 애플리케이션을 구성하는 중요한 한 가지 요소이고, 핵심기능에 부가되어 의미를 갖는 특별한 모듈을 가리킨다.

Aspect는 부가기능을 정의한 어드바이저와, 부가기능을 적용할 대상을 결정하는 포인트컷을 가지고 있다.

AOP 적용기술

프록시를 이용한 AOP

스프링은 프록시와 관련된 여러 기술을 조합하여 AOP를 지원한다. 따라서 자바의 기본 JDK와 스프링 컨테이너 이외에 특별한 기술과 환경이 요구되지않는다.

바이트코드 생성과 조작을 통한 AOP

AspectJ는 프록시를 사용하지 않는 대표적인 AOP 기술이다. 프록시를 이용한 방법처럼 간접적으로 타깃 오브젝트에 영향을 주는 것이 아니라 타깃 오브젝트를 직접 건드리는 방식을 통해 부가기능을 넣는다. 바이트코드를 통한 복잡한 방식을 통해 핵심기능 코드와 부가기능 코드가 함께 있었을 때 처럼 만들수 있다.

바이트코드 방식의 장점

  • 스프링과 같은 DI 컨테이너에 대한 의존 없이 AOP를 적용할 수 있다.
  • 프록시 방식보다 유연하고 강력한 AOP를 적용할 수 있다.
    • 오브젝트 생성, 필드 값의 조히와 조작, 스태틱 초기화 등 다양한 부가 기능을 부여할 수 있다.
    • 프록시 방식으로는 불가능한 private 메소드 호출, 스태틱 메소드 호출 등에도 부가기능을 부여할 수 있다.

바이트 코드 방식을 통한 AOP는 이러한 장점이 있지만 대부분의 AOP를 적용하기 위해서 프록시를 통한 방식도 충분하다.

AOP의 용어

  • 타깃: 부가기능을 부여할 대상으로 클래스 또는 다른 프록시 오브젝트이다.
  • 어드바이스: 타깃에게 제공하는 부가기능을 담은 모듈이다.
  • 조인 포인트: 어디바이스가 적용될 수 있는 위치이다. 스프링 프록시 AOP에서 조인 포인트는 메소드의 실행 단계 뿐이다.
  • 포인트컷: 어드바이스를 적용할 조인 포인트를 선별하는 작업이다.
  • 프록시: 클라이언트와 타깃 사이에 투명하게 존재하면서 부가기능을 제공하은 오브젝트이다.
  • 어디바이저: 포인트컷과 어드바이스를 하나씩 가지고 있는 오브젝트이다.
  • 애스펙트: AOP의 기본 모듈로서 하나 이상의 포인트컷과 어드바이스의 조합으로 만들어진다. 보통 싱글톤 오브젝트이다.

6.6 트랜잭션 속성

트랜잭션의 정의

트랜잭션은 더 이상 쪼갤 수 없는 최소 단위이며 commit()와 rollback()이 모두 이 트랜잭션 단위로 이루어져야 한다. 이 외의 트랜잭션 동작방식을 제어할 수 있는 조건이 있고 DefaultTransactionDefinition이 구현하는 TransactionDefintion 인터페이스는 다음 네 가지 속성을 정의한다.

1. 트랜잭션 전파(Transaction Propagation)

트랜잭션의 경계에서 이미 진행중인 트랜잭션이 있을 때 또는 없을 때 어떻게 동작할지 결정하는 방식이다. 두 개의 다른 트랜잭션이 다른 트랜잭션 경계를 가지고 있다면 다른 트랜잭션에 참여하여 실행될 수도 있고 아닐 수도 있다. 만약 같은 트랜잭션으로 실행된다면 역시 commit()와 rollback()에 대해 같은 단위로 적용된다.
이렇게 다른 트랜잭션 경계 설정을 가진 코드에 대해 이미 진행중인 트랜잭션이 어떻게 영향을 미치는지 정의하는 것을 트랜잭션 전파 속성이라고 한다.

트랜잭션 전파 속성

  • PROPAGATION_REQURIED
    • 진행 중인 트랜잭션이 없다면 새로 시작하고, 있다면 참여한다.
  • PROPAGATION_REQURIES_NEW
    • 항상 새로운 트랜잭션을 시작한다.
  • PROPAGATION_NOT_SUPPORTED
    • 트랜잭션이 없이 동작한다.
    • 특정 메소드만 트랜잭션의 적용을 피하고자 할 때 사용한다.

2. 격리 수준(Isolation Level)

서버 환경에서는 여러 개의 트랜잭션이 동시에 실행될 수 있고, 적절한 격리 수준을 두어 여러 트랜잭션이 동시에 실행되며 문제가 발생하지 않도록 해야 한다. 격리 수준은 기본적을 DB에 설정되어 있고, JDBC 드라이버나 DataSource 등에서 재설정할 수 있다.

3. 제한시간(Timeout)

트랜잭션을 수행하는 제한시간이다.

4. 읽기전용(Read Only)

읽기전용으로 설정하면 트랜잭션 내에서 데이터를 조작하는 시도를 막을 수 있고, 데이터 액세스 기술에 따라 성능 향상을 기대할 수 있다.

트랜잭션 인터셉터와 트랜잭션 속성

트랜잭션의 정의를 수정하기 위해서는 TransactinoDeinition 오브젝트를 DI 받아 사용할 수 있다. 하지만 이런 방법으로는 모든 트랜잭션의 정의가 한 번에 바뀌고, 메소드에 대해 선택적으로 적용할 수 없게 된다.
메소드 별로 다른 트랜잭션을 정의를 적용하기 위해 어드바이스의 기능을 확장할 수 있다. 메소드 이름 패턴에 따라 다른 트랜잭션 정의를 적용한다.

TransactionInterceptor

TransactionAdvice와 다르지 않고 PlatformTransactionManagerProperties 타입의 transactionAttributes를 가진다.
transactionAttributes는 트랜잭션의 네 가지 속성과 rollbackOn() 메소드를 가지는 TransactionAttribute 인터페이스로 정의된다.

스프링은 두 가지 종류의 예외 처리 방식이 있다. 런타임 예외가 발생하면 반드시 트랜잭션을 롤백한다. 체크 예외의 경우 이것을 예외 상황이 아닌 비즈니스 로직에 따른 의미 있는 리턴 방식으로 인식하여 트랜잭션을 커밋한다.
하지만 특정 체크 예외는 트랜잭션을 롤백해야 할 경우가 있고, rollbackOn() 메소드를 통해 어떤 체크 예외가 발생했을 때 롤백을 해야할 지 결정한다.

메소드 이름 패턴을 이용한 트랜잭션 속성 지정

첫 번째 속성인 트랜잭션 전파 항목은 필수적이고 나머지는 생략 가능하다. 생략된 속성은 모두 default 값을 갖는다.
예외 속성에서 '+'는 런타임 예외중에서도 해당 예외는 커밋하게 한다. '-'는 체크 예외이지만 해당 예외는 롤백되게 한다.

포인트컷과 트랜잭션 속성의 적용 전략

트랜잭션 포인트컷 표현식은 타입 패턴이나 빈 이름을 이용한다.

공통된 메소드 이름 규칙을 통해 최소한의 트랜잭션 어드바이스와 속성을 정의한다.

프록시 방식 AOP는 같은 타깃 오브젝트 내의 메소드를 호출할 때는 적용되지 않는다.

클라이언트가 인터페이스를 통해 타깃 오브젝트를 사용할 때는 프록시가 관여하고, 프록시의 방식의 AOP가 적용된다. 하지만 타깃이 직접 자신의 오브젝트를 호출하면 프록시는 관여하지 않게 되고 부가기능이 부여되지 않게 된다. 이 점을 유의하며 코드를 작성해야 한다.

이러한 문제를 해결할 수 있는 방법은 두 가지가 있다.

  • 스프링 API를 통해 프록시 오브젝트에 대한 레퍼런스를 가져와 같은 오브젝트의 메소드 호출도 프록시를 이용하도록 강제한다. 복잡한 과정을 거쳐야 하고, 비즈니스 로직이 있는 코드에 스프링 API 코드를 작성해야 하므로 권장되지 않는다.
  • AspectJ와 같은 타깃의 바이트코드를 직접 조작하는 AOP를 적용한다.

6.7 애노테이션 트랜잭션 속성과 포인트컷

세밀한 트랜잭션 속성의 제어가 필요한 경우 일일이 포인트컷과 어드바이스를 추가해 주어야 한다. 스프링에서는 이를 위해 직접 타깃에 트랜잭션 속성정보를 가진 애노테이션을 지정하는 방법을 제공한다.

트랜잭션 애노테이션

@Transactional

메소드, 클래스, 인터페이스에 애노테이션을 적용할 수 있고, 애노테이션이 적용된 모든 오브젝트에는 트랜잭션이 적용된다.

대체(fallback) 정책

메소드의 트랜잭션 속성을 확인할 때 타깃 메소드, 타깃 클래스, 선언 메소드, 선언 타입(클래스, 인터페이스) 순으로 확인한다.

0개의 댓글