달력

32024  이전 다음

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

IBM 에서 본 글인데... 블로그에 담으면 옆으로 퍼져서 안담았었는데..

DBGuide 에서 깨끗이 정리해놨네 ㅎㅎ

 

AOP와 메타데이터: 완벽한 조합, Part 1

메타데이터로 강화된 AOP의 개념과 구조

Ramnivas Laddad│IBM

J2SE 5.0의 일부인, 새로운 자바 메타데이터 장치는 지금까지 자바에 추가된 것 중 가장 중요한 것이다. 프로그램 엘리먼트에 추가 데이터를 첨부하는 표준 방식을 제공함으로서 메타데이터 장치는 설정 관리, 프레임웍 구현, 코드 생성 등 애플리케이션 개발의 많은 영역들을 단순화하고 향상시킬 수 있게 된다. 이 장치는 Aspect 지향 프로그래밍(AOP)에 중요한 영향을 끼친다.

메타데이터와 AOP의 결합은 중요한 질문들을 만들어낸다:

  • 메타데이터 장치가 AOP에 미치는 영향은 무엇인가?
  • 메타데이터로 강화된 AOP는 선택인가? 아니면 필수인가?
  • 메타데이터와 AOP를 효과적으로 사용하는 가이드라인은 무엇인가?
  • 이 두 결합이 AOP의 채택에 어떻게 영향을 주는가?

위 질문에 대한 대답을 AOP@Work 시리즈를 통해 설명하겠다. Part 1에서는 메타데이터의 개념과 새로운 자바 메타데이터 장치를 설명하겠다. 또한, 메타데이터를 제공하는 것과 이를 소비하는 것 사이의 차이를 설명하고 메타데이터 주석을 도입한 프로그래밍 시나리오도 소개하겠다. 그런 다음, AOP의 join point의 기초를 살펴보고 메타데이터 강조를 통해 얻을 수 있는 유익이 무엇인지를 설명하겠다. 마지막으로 실용 예제를 설명하면서 Part 1을 끝맺도록 하겠다. Part 2에서는, 다차원 비지니스 공간에서 하나의 신호로서 메타데이터를 보는 새로운 방식을 설명하겠다. 또한 AOP 채택에 메타데이터가 어떤 영향을 끼치는 지와 AOP와 메타데이터를 효과적으로 결합하는 가이드라인을 설명할 것이다.

이 글 전반에 걸쳐 제시된 모든 개념들은 세 가지 유명한 AOP 구현을 통해 현실화 될 것이다: AspectJ, AspectWerkz, JBoss AOP

메타데이터 개념

메타데이터는 데이터에 대한 데이터이다. 프로그래밍 언어라는 정황 속에서, 메타데이터는 메소드, 필드, 클래스, 패키지 같은 프로그램 요소들에 첨부된 추가 정보이다. 메타데이터는 주석(annotation)이라고 하는 프로그램 요소를 사용하여 표현된다. 메타데이터와 관련된 문법은 단순한 문서에서부터 실행-작동의 변경 까지 적용된다. 예를 들어, 클래스의 작성자와 저작권 소유자를 설명하는데 메타데이터를 사용할 수 있다. 프로그램 실행에 아무 영향이 없는 경우에 사용할 수 있고 또는 메소드의 작동을 변경할지도 모르는 트랜잭션 특성 같은 메소드 속성을 설명할 때도 이를 사용할 수 있다.

자바에는 다양한 메타데이터 툴들이 있는데(XDoclet은 가장 잘 알려져 있다), 메타데이터 주석은 Java 5.0과 함께 핵심 자바로 통합되었다. 자바 메타데이터 장치(JSR 175; 참고자료)에는 커스텀 주석을 자바 코드에 추가하는 장치가 포함되어 있고 리플렉션을 통해 메타데이터 주석으로 프로그래밍 방식의 접근을 제공한다.

메타데이터를 이해하고 사용하는 핵심은 공급과 소비의 개념이다. 메타데이터 공급자는 주석 인스턴스들과 프로그램 요소들을 제휴시키는 장치인 반면, 소비자는 주석 인스턴스를 읽고, 해석하고, 이에 대해 실행하는 것이다. 다음 섹션에서 이 개념들을 자세히 설명하겠다.

메타데이터 공급하기

메타데이터 장치는 주석을 프로그램 엘리먼트에 공급하는 장치를 정의한다. 선택적으로 메타데이터 장치는 주석 유형을 정의하는 방식을 지정할 수 있다. 주석 유형은 템플릿을 지정하여 주석 인스턴스를 만든다. 클래스가 템플릿을 지정하여 객체들을 생성하는 것과 많이 비슷하다. 메타데이터 장치는 주석을 소비하는 일반적인 방식을 지정하기도 한다. 다만 한가지, 주석과 연관된 해석과 문법을 정의하지 않는다. 이 일은 소비자의 몫이다.

메타데이터 장치들의 기능은 다양하다. 예를 들어, Javadoc 스팩은 프로그램 요소와 연관된 코멘트에 주석을 정의한다. 어떤 메타데이터 장치들은 개별 문서(주로 XML 문서)를 사용하여 메타데이터를 표현한다. 예를 들어, EJB 전개 디스크립터는 엔터프라이즈 빈의 추가 특성을 지정한다. Javadoc 장치와는 달리 이 접근방식은 프로그램 요소들과 메타테이터를 약결합한다. 단점은 개발자들이 같은 엘리먼트를 설명할 때 많은 소스들을 변경해야 한다.

자바 메타데이터 장치는 메타데이터에 새로운 언어 지원을 추가하여 주석 유형을 선언하고 프로그램 요소에 주석을 단다. 또한 클래스 파일의 소스 코드 레벨에 메타데이터를 보유하는 것도 가능하다. 런타임 시 보유 정책으로 제어된다.

메타데이터 공급자는 다중 엘리먼트에 주석을 개별적으로 공급하기 보다 크로스커팅 방식으로 특정 주석을 첨부하는 장치를 포함하고 있다. 이러한 특징 때문에 AOP는 주석을 잘 공급할 수 있다.

메타데이터 장치의 선택은 메타데이터를 표현하는 방식에 영향을 미치지만 추가 데이터들을 프로그램 엘리먼트에 연결시키는 근본적인 개념은 모든 메타데이터 장치들에 모두 일반적이다.

메타데이터 소비하기

메타데이터 주석에서 값을 만들기 위해서는 이를 소비하는 것이 필요하다. 메타데이터는 다양한 방식으로 소비될 수 있고 이러한 사용법을 이해하면 AOP와 메타데이터를 올바르게 결합할 수 있다. 다음의 사용 케이스들은 비 AOP 목적으로 제공된 주석이 AOP 구현에서 소비되는 방법을 이해하는데 도움이 된다.

코드 생성
코드 생성은 메타데이터를 사용하는 가장 익숙한 방식이다. XDoclet 같은 툴을 사용하여 Javadoc 태그로 지정된 주석을 소비하여 XML 문서 또는 자바 코드 같은 객체들을 만들 수 있다. 생성된 코드는 주석이 달린 엘리먼트의 런타임 작동에 영향을 준다. 자바 메타데이터 장치를 지원하는 새로운 XDoclet가 개발되고 있다. 명령행 유틸리티인 apt (Annotation Processing Tool)- Java 2 SDK 5.0의 일부-는 플러그인을 작성하여 주석을 처리하는 방식을 제공한다. 예를 들어, 최근에 발표된 Contract 실행 툴인 Contract4Jsms apt를 사용하여 design-by-contract (DBC) 콘트랙트를 실행하는 Aspect를 만든다.

프로그래밍 방식의 작동 변경
표준 메타데이터 장치는 런타임 시 사용할 수 있는 방식을 제공한다. 또한 프로그래밍 방식으로 리플렉션을 사용하여 주석 인스턴스에 접근할 수 있도록 한다. 주석 인스턴스는 다른 객체들 처럼 사용되어 프로그램의 작동을 변경한다. 이 같은 프로그래밍 방식의 소비는, 생성된 객체들이 이 주석에서 인코딩된 정보를 읽힐 수 있는 곳에서, 프로그래머들이 애플리케이션의 코드 생성 루트를 지나치도록 한다.

프레임웍의 소비
메타데이터는 프로그램 엘리먼트와 프레임웍 간 통신, 또는 EJB, EMF, TestNG 같은 툴을 활용하는데 일반적으로 사용된다. 프레임웍 자체는 코드 생성, 리플렉션을 사용한 접근, 또는 AOP를 사용하여 특정 로직을 실행에 적용한다. @Remove@Session같은 EJB 3.0에서의 주석의 사용은 프로그램 엘리먼트의 역할인 프레임웍과 통신한다. Eclipse Modeling Framework은 주석을 사용하여(현재 Javadoc 태그로 표현됨) UML 모델을 만들고 XML 영속성을 지원한다. TestNG는 메타데이터를 테스트 케이스와 테스트 실행 툴들 간 통신에 사용한다.

언어 확장
메타데이터의 사용은 기저의 프로그래밍 언어와 컴파일러로 확대된다. 문법 속성들을 메타데이터와 제휴 시킨다는 것은 제휴하지 않은 것과는 다른 구조와 작동을 갖게 된다는 것을 의미한다. (참고자료) 최근에 발표된 Pluggable Annotation Processing API (JSR 269 )는 표준 방식으로 주석을 처리한다. 자바를 확장하는 메타데이터의 사용은 강력하면서도 위험하다: 주석은 핵심 언어를 변경하지 않고 자바 언어에 새로운 기능을 추가할 수 있도록 해주어 이를 개방 언어로 만든다. 최고의 시나리오에서 원리를 갖춘 확장은 호스트 언어의 몇 가지 한계를 극복 할 수 있다. 한편 비표준 또는 통일성 없는 주석들은 이해가 안되는 코드를 만들 뿐이다.

평범한 객체 지향 언어에서 AOP를 실행시키는 것은 언어 확장에 메타데이터를 사용하는 한 가지 예이다. AspectWerkz와 JBoss AOP는 메타데이터를 사용하여 Aspect로서 클래스의 문법을, pointcut으로서 데이터 필드를, 어드바이스로서 메소드를 변경한다. AspectJ 5는 @AspectJ 문법을 지원하는데 이는 AspectJ와 AspectWerkz 프로젝트의 합병 결과이다. 다른 AOP 구현과 자바 언어 확장에 대한 자세한 정보는 참고자료를 참조하라.

다음 섹션에서는 AOP의 join point 모델의 기초를 설명하고 메타데이터로 강화하는 것의 효용을 설명하겠다.

메타데이터와 join point 모델

A join point는 시스템 실행 시 구분 포인트이다. AOP의 가장 근본적이면서 독창적인 개념인 join point model모델은 시스템에서 어떤 join point가 노출되고 이들이 어떻게 잡히는지를 정의한다. Aspect를 사용하여 크로스커팅 기능을 구현하려면 pointcut이라고 하는 프로그래밍 구조를 사용하여 필요한 join point를 잡아야 한다.

Pointcuts은 join point를 선택하고 선택된 join point에서 콘텍스트를 모은다. 모든 AOP 시스템은 pointcut을 정의하는 언어를 제공한다. pointcut 언어의 난해함은 다양한 AOP 시스템들 간 요소를 차별화한다. pointcut 언어가 성숙할수록 강력한 pointcut을 작성하기는 더 쉽다.

join point 잡기

pointcut은 프로그램의 기존 엘리먼트의 속성을 지정한다. 이 작업의 주요 부분과 좋은 Aspect를 작성하는 원리는 강력한 pointcut을 작성하는데 있다; 다른 요소로는 설계가 잘 된 Aspect이다. 기대했던 것보다 더 많은 join point를 잡거나 기대했던 join point를 놓치는 pointcut은 시스템 진화의 걸림돌이다. 좋은 pointcut을 작성하는 것이 AOP를 파악하는 핵심이다.

현재 join point를 잡는 가장 일방적인 방식은 프로그램 엘리먼트의 임의의 속성들을 활용하는 것이다. 서명(signature) 같은 정적 속성들(유형과 메소드 이름, 인자 유형, 리턴 유형과 예외 유형 등)과 어휘 배치와 제어 흐름 같은 동적 속성들이 이에 포함된다. join point 서명의 와일드 카드들을 공정하게 사용하면 훌륭한 pointcut 정의가 된다. 개별 pointcut 들을 합성하여 보다 복잡한 것을 만들 수 있다. 프로그램 엘리먼트의 임의 속성에 기반한 join point 모델은 강력하고 유용하다. 여러 제품에서 AOP의 성공이 이를 증명하고 있다.

프로그램 엘리먼트의 서명에 사용할 수 있는 임의의 정보는 필요한 join point를 잡기에 충분하다. 이 모델에서 동적 크로스커팅이라고 하는 임의 데이터의 결합인 와일드카드와 제어 흐름 같은 동적 속성들은 잡힌 프로그램 엘리먼트를 변경하지 않고 join point를 잡을 수 있다. 예를 들어 Remote 인터페이스를 구현하는 클래스에서 RemoteException을 던지는 연산을 지정하여 모든 RMI 호출을 잡을 수 있다. (AspectJ에서 정의된) execution(* Remote+.*(..) throws RemoteException) 같은 pointcut은 프로그램 엘리먼트를 변경하지 않고 모든 RMI 연산을 깔끔하게 잡고 강력한 pointcut도 보장한다. 가장 큰 장점은 RMI 인프라가 필요로 하는 것 외에 추가 협업 없이 join point를 잡을 수 있다는 점이다.

메타데이터로 join point 잡기

서명 기반의 pointcut은 특정 크로스커팅 객체를 구현하는데 필요한 join point를 잡을 수 없다. 예를 들어, 트랜잭션 관리 또는 권한을 필요로 하는 join point를 어떻게 잡겠는가? RMI 예와는 달리, 엘리먼트의 이름 또는 서명에 내재된 어떤 것도 트랜잭션 또는 권한 특성을 암시하지 않는다. 이 상황에 필요한 pointcut은 다음 예제에서 직접 보겠지만 다루기 힘들다. (이 예제는 AspectJ에 있지만 다른 시스템의 pointcut은 개념상 동일하다.)

pointcut transactedOps()     : execution(public void Account.credit(..))      || execution(public void Account.debit(..))      || execution(public void Customer.setAddress(..))        || execution(public void Customer.addAccount(..))      || execution(public void Customer.removeAccount(..));

이 같은 상황은 메타데이터 사용을 도입하여 필요한 join point를 잡는다. 예를 들어, 아래에서 보는 것 처럼 pointcut을 작성하여 @Transactional 주석을 실행하는 모든 메소드의 실행을 잡을 수 있었다.

pointcut execution(@Transactional * *.*(..));

메타데이터와 모듈성

위 예제에서 메타데이터를 사용하여 join point를 잡는 것이 어리석은 것 처럼 보이지만 그 같은 사용 방식의 함축성을 생각하는 것은 중요하다. 특히 모듈성에 적용될 때는 더욱 그렇다. 일단 pointcut에 메타데이터를 사용하기 시작하면 메소드는 해당 주석을 실행하여 이들을 사용하는 Aspect의 크로스커팅 구현에서 협업해야 한다:

public class Account {    ...    @Transactional(kind=Required)    public void credit(float amount)  {        ...    }    @Transactional(kind=Required)    public void debit(float amount)  {        ...    }	    public float getBalance() {        ...    }    ...}

이와 비슷하게, Customer클래스의 addAccount(), removeItem(), setAddress() 메소드는 이제 @Transactional 주석을 실행해야 한다.

대부분의 AOP 사용자들은 Aspect 특성을 활용하는 디자인 패턴을 통해 현재 기존 AOP 지원에 트랜잭션과 권한부여 부분을 구현하고 있다. 이 글에서 보듯, AOP 시스템에 메타데이터를 추가하면 시스템을 놀랍게 향상시킬 수 있다. 추가된 메타데이터가 AOP 시스템의 모듈성에 어떻게 영향을 미치는 지와 메타데이터가 가장 유용하게 사용될 시나리오는 Part 2에서 다루겠다. 다음 섹션에서는 AOP 구현이 결합 메타데이터로 확장되는 방법을 구체적으로 설명하겠다.

메타데이터로 강화된 AOP

AOP 시스템과 이들의 join point 모델은 메타데이터 주석을 소비함으로서 증가할 수 있다. JBoss AOP, Spring AOP, AspectWerkz, AspectJ는 메타데이터를 활용하는 메커니즘을 제공하고 있거나 제공할 계획이다. JBoss AOP와 AspectWerkz는 현재 버전에 메타데이터를 지원한다. Spring AOP는 pointcut을 프로그램방식으로 작성할 수 있도록 하고 org.springframework.aop.Pointcut 인터페이스를 구현하는 것으로 메타데이터를 지원한다. AspectJ의 차기 버전은 AspectJ 언어를 변경함으로서 메타데이터를 지원할 것이다.

이전 섹션에서 @Transactional 주석으로 메소드를 집어내는 예제를 사용하여 AOP 시스템이 메타데이터를 소비하는 방법의 기초를 설명했다. 이제 부터는 AOP와 메타데이터를 결합하는 부분에 초점을 맞춰 설명하겠다.

이 글의 예제가 메타데이터를 지원하는 AOP 구현에 초점을 맞추고는 있지만 핵심 AOP 시스템이 직접 이를 지원하지 않을 때도 메타데이터를 소비하는 것이 가능하다. 코드 생성 지원에 피기백을 활용한다. 예를 들어, Barter는 오픈 소스 툴로서, 주석과 코드 생성 사전 단계를 사용해서, Javadoc 태그에 기반한 join point 캡쳐를 지원하지 않는 AspectJ의 구 버전과의 DBC 콘트랙트를 실행한다. 요즘, Contract4J는 자바 메타데이터 장치 스타일의 주석을 사용하여 이와 비슷한 일을 수행한다.

AOP 시스템의 메타데이터 지원

메타데이터 기반의 크로스커팅을 지원하려면 AOP 시스템은 주석을 소비하고 공급하는 방식을 제공해야 한다. 여기에서 두 가지 지원 유형의 기초를 설명하겠다. 다음 섹션에서 보다 자세히 설명하겠다.

주석 소비 지원
주석 소비를 지원하는 AOP 시스템에서는 프로그램 엘리먼트와 제휴된 주석에 기반하여 join point를 선택할 수 있다. 이 같은 지원이 되는 현재 AOP 시스템에서는 다양한 서명 패턴의 정의를 확장하여 주석 유형과 속성들이 지정되도록 한다. 예를 들어, pointcut은 Timing유형의 주석을 실행하는 모든 메소드를 선택한다. 게다가 25를 초과하는 속성값을 지닌 메소드만 하위로 선택할 수 있다. 주석 유형과 속성에 의존한 어드바이스를 구현하려면 시스템은 join point와 제휴된 주석 인스턴스를 잡는 pointcut 신택스를 추가할 수 있다. 마지막으로, 시스템은 어드바이스가 반사적인 API를 통해 주석 인스턴스에 접근할 수 있도록 한다.

주석 공급 지원
표준 자바 메타데이터 장치를 사용하여 하나의 주석 인스턴스는 주석이 달리고 있는 각 프로그램 엘리먼트를 위해 선언된다. 하지만 다중의 프로그램 엘리먼트들에 같은 주석 선언이 나타난다면 불필요한 것이 되고 만다. AOP 크로스커팅 메커니즘을 사용하여 모든 효력이 적용되는 엘리먼트에 한번만 주석을 선언하도록 할 수 있다. 주석 공급을 지원하는 AOP 시스템에서는 크로스커팅 방식으로 프로그램 엘리먼트에 주석을 붙일 수 있다. 예를 들어, @Secure 주석을 간단한 선언과 함께 Account 클래스의 모든 메소드에 붙일 수 있다. 모든 메소드에 걸쳐 주석을 낭비할 필요가 없다.

모든 AOP 시스템이 앞서 언급한 모든 기능들을 지원하는 것은 아니다. 이어지는 섹션을 통해 보다 자세히 배우게 될 것이다. 여러 AOP 시스템들이 어떻게 주석 소비를 지원하는지 볼 것이다.

AOP에서 주석 소비하기

pointcut 신택스는 다양한 메타데이터가 강화된 AOP 시스템과는 다르다. @Transactional 주석 인스턴스를 수반하는 모든 메소드를 잡는 pointcut을 각 시스템이 어떻게 핸들하는지를 연구하면 그 차이를 알 수 있다. 이 예제에서는, 주석 유형 별로 join point를 선택하는 것에 초점을 맞추겠다; 그런 다음 join point 선택에 영향을 미치는 다른 요소들을 설명하겠다.

AspectJ5
AspectJ 5 신택스는 유형, 메소드, 필드에 대한 서명의 정의를 확장하여 메타데이터 주석을 서명의 일부로 포함시킨다. 다음과 같다:

pointcut transactedOps(): execution(@Transactional * *.*(..)); 

AspectJ 5에서 @AspectJ 스타일의 정의를 사용한다면 같은 pointcut이 다음과 같이 정의된다:

@Pointcut("execution(@Transactional * *.*(..))")void transactedOps();

AspectWerkz
대부분의 다른 AOP 툴과 마찬가지로, AspectWerkz의 pointcut 신택스는 AspectJ의 그것과 닮았다. XML 스타일이 동일한 pointcut 식을 갖고 있지만 다음 코드에서 pointcut 선언은 메타데이터 유형의 주석을 사용한다:

@Expression("execution(@Transactional * *.*(..))")Pointcut transactedOps;

AspectWerkz는 메타데이터를 사용하여 AOP를 지원하도록 자바를 확장한다. 따라서 AspectWerkz는 프로그래밍 엘리먼트의 문법을 확장하기 위해서 그리고 메타데이터에 기반한 크로스커팅을 구현하기 위해서 메타데이터를 사용한다. 위 예제에서 후자의 용법에 초점을 맞추었다.

JBoss AOP
JBoss AOP는 다른 신택스를 사용하지만 기타 AOP 시스템들과 개념적으로는 많이 다르지 않다. 이곳에 나타난 pointcut은 다른 예제와 동일하지만 Jboss의 XML 신택스로 표현된다:

<pointcut name="transactedOps" expr="* *->@Transactional(..)"/>

다른 AOP 시스템들이 첨부된 메타데이터 주석에 근거하여 join point를 잡는 방법은 개념적으로 차이가 별로 없다.

주석 속성별로 선택하기

join point를 선택할 때 유형만 고려해서는 안된다. 속성 역시 고려되어야 한다. 예를 들어, 다음의 pointcut은 RequiredNew로 설정된 속성 값을 가진 @Transactional 주석을 가진 모든 메소드들을 잡는다:

execution(@Transactional(value==RequiredNew) *.*(..)) 

작성할 때, 어떤 AOP 시스템도 주석 속성에 기반한 pointcut을 지원하지 않고 대신 각 시스템은 어드바이스 내부의 동적 결정에 근거하여 속성들을 검사하고 해당 로직을 호출한다. (또는 적어도 if() pointcut을 사용하여 동적 체크를 사용한다.) 주석 속성 기반의 pointcut은 컴파일 시간 체크와 정적 선택에 특히 유용하다. AOP 시스템의 차기 버전들은 이러한 종류의 pointcut을 지원할 것이다.

주석 인스턴스 노출하기

어드바이스 로직이 메타데이터 주석 유형인스턴스 속성에 의존하기 때문에 각 주석 인스턴스는 join point의 다른 콘텍스트와 같은 방식으로 콘텍스트로서 노출되어야 한다. (예를 들어, 객체, 메소드 인자 등). AspectJ 5는 기존 pointcut을 확장하고 새로운 것을 추가하여 주석을 노출한다. 예를 들어, 다음의 pointcut은 캡쳐된 join point와 제휴된 Transactional유형의 주석 인스턴스를 모은다:

pointcut transactedOps(Transactional tx)    : execution(@Transactional * *.*(..)) && @annotation(tx);

일단 캡쳐되면, 주석 인스턴스는 다른 콘텍스트와 같은 방식으로 사용된다. 예를 들어, 다음 어드바이스에서 캡쳐된 주석은 이것의 속성을 묻고있다:

Object around(Transactional tx) : transactedOps(tx) {    if(tx.value() == Required) {        ... implement the required transaction behavior    } else if(tx.value() == RequiredNew) {        ... implement the required-new transaction behavior    }    ...}

대부분의 AOP 시스템은 반사적인 API만을 사용하여 캡쳐 된 join point에서 주석 인스턴스를 노출하고 주석들을 join point 콘텍스트로서 바인딩하지 못하도록 한다. 이 경우, 이러한 제휴 주석들에 대한 권장 join point를 나타내는 객체를 쿼리할 수 있다. AspectJ는 반사 액세스와 전통적인 join point 콘텍스트를 제공한다.

AOP에서 주석 공급하기

AOP 구조를 사용하여 주석을 공급하는 기본 개념은 프로그램 엘리먼트의 정의를 주석으로 어지럽히지 않는다는 것이다. 개념상, 그와 같은 구조에서는 크로스커팅 방식으로 프로그램 엘리먼트에 주석을 붙일 수 있다. 언뜻 보기에, AOP 구조를 사용하여 주석을 공급하고 그런 다음 그 주석을 사용하여 join point를 잡는 것은 불필요한 것 처럼 보인다. 결국 주석을 필요로 하는 join point를 구별할 수 있다면 pointcut을 작성하고 이들에게 join point를 직접 권고할 수 있다. 하지만 크로스커팅 방식으로 주석을 공급하는 것은 매우 도움이 된다. 우선, 그 같은 선언은 비 AOP 클라이언트들과 통신할 때 연결관으로서 작동한다. 더욱이, 크로스커팅 메커니즘으로 주석을 공급하면 주석 클러터(clutter)를 피하면서 약결합 시스템을 설계할 수 있다.

AOP 구조를 사용하는 다른 디자인에 대해서는 이 글 말미에 설명하도록 하겠다. 이제는 크로스커팅 방식으로 주석을 공급하는 기본 신택스를 설명하겠다.

주석 신택스 공급하기

AspectJ의 제안 신택스는 현재 정적 크로스커팅 구조를 확장하여 새로운 declare annotation 신택스를 만든다. 다음 코드는 Authenticated 유형의 주석을 banking으로 설정된 permission 속성과 함께 붙인다:

declare annotation : * Account.*(..)                    : @Authenticated(permission="banking");

@AspectJ pointcut은 같은 선언이 다음과 같이 작성되는 곳에서 @DeclareAnnotation 사용하여 같은 기능을 지원한다:

@DeclareAnnotation("* Account.*(..)")@Authenticated(permission="banking") void bankAccountMethods();

JBoss AOP의 경우, XML 스타일의 aspect를 사용할 때 annotation-introduction 엘리먼트를 사용하여 주석을 붙인다. 주석이 런타임시 보유되지 않는다면 invisible 속성이 지시한다. (표준 자바 메타데이터의 RetentionPolicy.SOURCE에 해당).

<annotation-introduction expr="method(* Account->*(..))"                         invisible="false">      @Authenticated(permission="banking")</annotation-introduction>

주석 공급의 원리는 신택스가 다르더라도 다양한 AOP 시스템 두루 비슷하다.

메타데이터를 이용한 AOP 디자인

메타데이터와 AOP를 결합하는 것은 매우 단순하다. 중요한 것은 메타데이터 기반 크로스커팅을 어디에 적용하고 어디에 적용하지 않아야 하는지를 아는 것이다. join point의 임의의 속성들을 사용하는 AOP 구현에서 메타데이터 기반의 pointcut을 결합하는 것 까지 시스템이 어떻게 진화하는지를 설명하는 것으로 위 질문에 대한 답을 하겠다. Part 2에서는 메타데이터 중심의 접근 방식을 선택할 때의 고려 사항들을 자세히 설명하겠다.

이 섹션의 논의는 두 가지 측면에서 유용하다. 우선, 메타데이터를 사용하는 것이 언제나 AOP 사용자들의 우선순위는 아니라는 것을 이해해야 한다. 둘째, 이 글의 예제 구현은 메타데이터 기반의 크로스커팅을 적용하기로 결정했을 때 디자인을 개발하는 방법에 대한 가이드라인일 뿐이다.

이 예제는 트랜잭션 관리 프로그램이다. AspectJ를 사용하여 예제의 모든 코드를 개발한 반면 다른 AOP 시스템들의 구현은 개념적으로 동일하다. 예제의 각 단계들은 원래 디자인의 리팩토링으로 간주하라. 목표는 시스템을 서서히 디커플(decouple)하여 모듈성을 향상시키는 것이다.

Version 1: 순진한 aspect

크로스커팅 문제를 모듈화하는 내 첫 번째 시도는 pointcut 정의 뿐만 아니라 pointcut에 대한 어드바이스를 포함하고 있는 시스템 스팩의 aspect를 적용하는 것이다. 이것은 매우 간단한 스키마이고 AOP를 배울 때 처음 만나게 되는 디자인이기도 하다. 그림 1은 하나의 aspect를 적용하는 디자인의 스키마이다:

그림 1. AOP를 사용하여 트랜잭션 관리를 구현하기

Listing 1은 위 디자인을 구현한 것이다:

Listing 1: 은행 시스템을 위핸 트랜잭션 관리 aspect
public aspect BankingTxMgmt {    pointcut transactedOps()         : execution(void Customer.setAddress(..))          || execution(void Customer.addAccount(..))          || execution(void Customer.removeAccount(..))          || execution(void Account.credit(..))          || execution(void Account.debit(..));              Object around() : transactedOps() {         try {             beginTransaction();             Object result = proceed();             endTransaction();             return result;         } catch (Exception ex) {             rollbackTransaction();             return null;         }    }        ... implementation of beginTransaction() etc.}

이 스키마는 기반 시스템에 대한 지식이 별로 요구되지 않는 aspect에 잘 맞는다. 예를 들어, 풀링(pooling)을 실행하고자 한다면 풀링되고 있는 리소스의 생성과 소멸에 대한 호출을 권고하는 제너릭 aspect를 작성할 수 있다. 하지만 제너릭 방식으로는 필요한 join point를 잡을 수 없는 크로스커팅 기능의 경우 이 방식은 한계가 있다. 우선, pointcut 정의가 시스템 스팩이기 때문에 aspect는 재사용 될 수 없다. 둘째, 시스템을 변경하면 aspect도 변경해야 한다. 다시 말해서, 첫 번째 버전의 시스템은 프로그램 엘리먼트들 간 N to 1 커플링과 pointcut을 남긴다. 이것이 최상의 옵션이 아니기 때문에 다른 시도가 필요하다.

Version 2: 재사용 가능한 aspect

내 두 번째 시도는 aspect를 재사용 할 수 있는 것으로 향상시켰다. aspect의 재사용 부분을 추출하여 시스템 스팩의 방식으로 pointcut을 정의하는 하위 aspect를 추가했다. 그림 2는 기본 aspect가 추출된 후의 구조이다:

그림 2. 재사용 할 수 있는 트랜잭션 관리 aspect 추출하기

Listing 2는 재사용이 가능한 기본 aspect이다. Listing 1과 비교해 보면 두 가지 변화를 알 수 있다: aspect는 추상적인 것으로 표시되고 transactedOps() pointcut은 정의가 제거된 채로 추상적인 것으로 표시된다:

Listing 2. 재사용 가능한 트랜잭션 관리 기본 aspect
public abstract aspect TxMgmt {    public abstract pointcut transactedOps();               Object around() : transactedOps() {         try {             beginTransaction();             Object result = proceed();             commitTransaction();             return result;         } catch (Exception ex) {             rollbackTransaction();             return null;         }    }       ... implementation of beginTransaction() etc.}

다음에, 기본 aspect에 대한 하위 aspect를 작성해야 한다. 아래 하위 aspect는 트랜잭션 관리 지원을 필요로 하는 join point를 캡쳐하는 pointcut을 정의한다. Listing 3은 Listing 2의 TxMgmt aspect를 확장한 은행 스팩의 하위 aspect이다. 하위 aspect는 transactedOps() pointcut을 Listing 1에 쓰였던 것과 똑 같은 것으로 정의한다.

Listing 3. 시스템 스팩의 하위 aspect
public aspect BankingTxMgmt extends TxMgmt {    public pointcut transactedOps()         : execution(void Customer.setAddress(..))          || execution(void Customer.addAccount(..))          || execution(void Customer.removeAccount(..))          || execution(void Account.credit(..))          || execution(void Account.debit(..));}

이것은 향상되기는 했지만 이 디자인 역시 하위 aspect와 클래스들 간 N-to-1 의존형이다. 은행 시스템에 필요한 트랜잭션 요구 사항들이 변경되면 BankingTxMgmt의 pointcut 정의도 변경되어야 한다. 이상적이지 못하다.

Version 3: Participant 패턴

위에서 재사용성의 문제를 다루었지만 N-to-1 의존성은 여전히 남아있다. Participant 패턴을 적용하여 이를 의존성을 제거할 수 있다. 전체 시스템에 하나의 하위 aspect를 사용하는 대신 많은 하위 aspect를 사용하면서—하위 시스템 당 하나— 비교적 안정적인 pointcut을 작성할 수 있다. 이 상황에서 하위 시스템은 패키지, 패키지 세트, 심지어는 클래스가 될 수 있다. 그림 3은 다른 엘리먼트들 간 구조 관계이다:

그림 3. participant 디자인 패턴 적용하기

Listing 4는 임베디드 클래스를 위한 pointcut을 정의하는 participant aspect를 가진 Customer 클래스이다.

Listing 4. 임베디드 participant aspect를 가진 Customer 클래스
public class Customer {    public void setAddress(Address addr) {        ...    }    public void addAccount(Account acc) {        ...    }    public void removeAccount(Account acc) {        ...    }    ...    private static aspect TxMgmtParticipant extends TxMgmt {        public pointcut transactedOps()             : execution(void Customer.setAddress(..))              || execution(void Customer.addAccount(..))              || execution(void Customer.removeAccount(..));    }}

Customer 클래스 예제에서 하위 aspect는 인자에 대한 와일드카드를 가진 모든 메소드들을 열거한다. 실제로 pointcut 정의를 단순화 하는데 와일드카드를 사용하게 될 것이다. 예를 들어 이 클래스의 모든 퍼블릭 메소드들을 다음 정의를 사용하여 transactedOps() pointcut으로 캡쳐되도록 선언할 수 있다:

public pointcut transactedOps()     : execution(public * Customer.*(..));

Listing 5에서, 시스템의 트랜잭션 관리 기능에 참여하기 위해 Account 클래스가 어떻게 하위 aspect를 삽입하는지를 보여주고 있다.

Listing 5. 삽입된 participant aspect를 가진 Account 클래스
public class Account {    public void credit(float amount) {        ...    }    public void debit(float amount) {        ...    }    public float getBalance() {        ...    }    ...    private static aspect TxMgmtParticipant extends TxMgmt {        public pointcut transactedOps()             : execution(void Account.credit(..))              || execution(void Account.debit(..));    }}

Customer 클래스처럼, 단계를 추가하면 pointcut을 단순화 할 수 있다. 예를 들어, getBalance() 메소드를 제외한 모든 퍼블릭 메소드들이 트랜잭션 관리 시 실행되어야 한다는 것을 깨달았다면? 이러한 깨달음을 적용하여 pointcut을 정의할 수 있었다:

public pointcut transactedOps()    : execution(public void Account.*(..))     && !execution(float Account.getBalance());

클래스가 변경되면 이 클래스에서 중첩된 하위 aspect만 변경해야 한다. 큰 N 대신 시스템 커플링을 각 하위 aspect에서 캡쳐 된 더 적은 프로그램 엘리먼트의 수인 n-to-1으로 시스템 커플링을 줄였다. 더욱이 트랜잭션 관리 필요성과 관련하여 클래스가 변경되면 삽입된 participant aspect의 pointcut만 변경하여 지역성을 보존한다.

이 예제는 AOP 관련 논의에서 종종 놓치는 중요한 부분을 설명하고 있다. 전체 시스템에 대한 서명 패턴을 찾으려 한다면 불쾌한 상황 즉, 불안정하고 복잡하며 부정확한 pointcut을 만나게 된다. 이 시스템의 하위 세트를 고려할 때 그 하위 시스템과 잘 작동되는 신호 패턴을 종종 찾을 수 있다. Participant 패턴을 클래스 당 하나의 aspect와 함께 사용하는 것은, 하위 시스템으로의 논리적 구분은 잘 이루어 지겠지만, 클래스를 하위 시스템으로 간주하는 것이다.

이 솔루션은 대부분의 상황에서 적용된다. 단점은 클래스들이 기본 aspect에 대한 직접적인 의존성이 있어서 기본 aspect가 언제나 시스템에 나타나야 한다는 점이다. 이 솔루션의 또 다른 문제는, 클래스가 중첩된 하위 aspect들을 삽입하여 협업에 명확히 참여하지 않는 한, 이것의 크로스커팅 기능이 적용되지 않는 다는 것이다. 이 문제는 솔루션 보다는 크로스커팅 기능의 본질을 다루어야 하는 문제인 것이다.

Version 4: 메타데이터 기반 pointcut

각 메소드를 변경하여 주석을 갖게 하고 시스템 당 하나의 하위 aspect가 되도록 할 예정이다.(Version 2와 유사) 하지만 나의 하위 aspect는 메타데이터 기반의 pointcut을 사용하여 필요한 join point를 잡을 것이다—메소드는 내가 공급한 주석을 실행할 것이다. 하위 aspect 자체는 시스템에 걸쳐 재사용될 것이다. 그림 4는 이 버전의 스키마이다.

그림 4. 메타데이터 기반 트랜잭션 관리

메타데이터 기반의 하위 aspect를 사용할 때, 클래스의 join point가 그 특성을 변경하면 해당 join point에 대한 주석만 변경되어야 한다. Listing 6는 Version 2에서 TxMgmt aspect를 확장한 하위 aspect이고 Transactional 유형의 주석을 수행하는 모든 메소드를 잡아서 transactedOps() pointcut을 정의한다.

Listing 6. 메타데이터 기반 트랜잭션 관리 하위 aspect
public aspect MetadataDrivenTxMgmt extends TxMgmt {    public pointcut transactedOps()         : execution(@Transactional * *.*(..));}

이 클래스는 Transactional 유형의 주석을 트랜잭션 관리 시 실행되어야 하는 모든 메소드에 첨부함으로서 하위 aspect와 협업해야 한다. Listing 7은 이 Customer 클래스를 주석을 수행하는 메소드와 함께 구현한 것이다:

Listing 7. Customer 클래스와 주석
public class Customer {    @Transactional    public void setAddress(Address addr) {        ...    }    @Transactional    public void addAccount(Account acc) {        ...    }    @Transactional    public void removeAccount(Account acc) {        ...    }    ...}

Listing 8은 유사한 Account클래스의 구현이다:

Listing 8. Account 클래스와 주석
public class Account {    @Transactional    public void credit(float amount) {        ...    }    @Transactional    public void debit(float amount) {        ...    }    public float getBalance() {        ...    }    ...}

이 지점에서 메소드와 협업 aspect 간 일대일 의존성을 확립했다. 또한 aspect와 클래스 간 직접적인 의존성도 제거했다. 결과적으로 기본 aspect만 변경하고자 할 때 시스템에 변경을 하지 않고도 이를 수행할 수 있다.

기본 aspect의 사용은 선택적이다. (계층을 무너뜨릴 수 있다.) 하지만 기본 aspect를 메타데이터 기반의 하위 aspect에서 분리하는 것은 몇 가지 장점이 있다. 우선, 분리된 aspect는 주석 유형을 선택할 수 있다. 한 시스템에서 주석 유형으로 Transactional을 사용하여 join point를 잡을 수 있다. 이때 다른 시스템의 주석 유형은 Tx가 될 수 있다. 둘째, Participant 패턴과 메타데이터 기반의 방식 간 선택을 분리된 aspect로 나아간다. 셋째, 이 방식을 사용하여 @Purchase 또는 @OrderProcessing같은 비지니스 주석들에서 트랜잭션 pointcut을 분리할 수 있다. 마지막으로 메타데이터 기반의 방식과 Participant 패턴 기반의 방식을 결합할 수 있다.

주석을 통해 협업함으로서 참여에 대한 책임은 (참여하는 하위 aspect 대신) 개별 메소드로 옮겨간다. MetadataDrivenTxMgmt와 클래스들 간 의존성은 주석 유형과 관련 문법으로 제한된다.

반복보다 좋은 것은 없다. 하지만 한 가지 특별한 시나리오가 있다. 최적의 결과를 위해 한 단계 더 들어간 디자인을 리팩토링 할 수 있는 시나리오이다.

Version 5: 메타데이터 공급자로서의 Aspect

특정 상황에서, 클래스 내 대부분의 메소드들은 (Version 4의 주석 같은) 주석을 수행해야 한다. 더욱이 많은 크로스커팅 특성들은 메소드 당 한 개 이상의 주석을 필요로 한다. 이 상황은 모든 메소드에 대해 선언된 많은 주석들을 주석 지옥으로 설명되는 현상으로 몰고 간다. 주석 아수라장은 Participant 패턴과 주석자-공급자 aspect를 결합함으로서 줄일 수 있다. 이는 참여하는 join point를 표현하는 분명한 방식이 있을 때 유용한 옵션이다. 이 같은 경우, 주석자-공급자 디자인은 join point에 대해 주석을 놓치는 위험을 피한다.

그림 5. 메타데이터 공급자로서의 Aspect

주석자 aspect는 한 개 이상의 declare annotation들을 사용한다: 예를 들어, declare annotation : <Method pattern> : <Annotation definition>;. >;. 이 예제에서, 클래스에 Participant 패턴과 비슷한 협업과 하나의 주석자 aspect를 사용했다. 하지만 이렇게 하는 것이 이 디자인의 근본적인 요구사항은 아니다. 예를 들어 패키지 당 하나의 주석자 aspect를 구현할 수 있다. 핵심 개념은 필요한 join point를 캡쳐하고 그 클래스의 주석 아수라장 현상을 피하는 명확한 서명 패턴 또는 동적 콘텍스트 정보(제어-흐름 등)를 가진 알맞은 하위 시스템을 찾는 것이다. Listing 9는 주석자 aspect를 가진 Customer 클래스 이다.

Listing 9. 삽입된 주석자 aspect를 가진 Customer 클래스
public class Customer { public void setAddress(Address addr) { ... } public void addAccount(Account acc) { ... } public void removeAccount(Account acc) { ... } ... private static aspect Annotator { declare annotation: public Customer.*(..): @Transactional; } }

Listing 10의 Account 클래스도 이와 비슷하다.

Listing 10. 삽입된 주석자 aspect를 가진 Account 클래스
public class Account {    public void credit(float amount) {        ...    }    public void debit(float amount) {        ...    }    ...    private static aspect Annotator {        declare annotation: public Account.*(..): @Transactional;    }}

이제 이 구현과 Version 3에서 Participant 패턴을 사용하는 구현을 비교해보자. 이 버전은 한 가지 중대한 단점이 있다: 이것은 클래스를 특정 aspect로 묶는다. 이는 매우 적극적인 개입이다—기본 aspect는 언제나 존재해야 한다. (이는 모든 참여 aspect에게 있어 기본 aspect이기 때문이다.) 주석자 aspect 접근방식을 사용하여 참여는 공통으로 이해된 주석 유형의 레벨에서만 발생할 수 있다.

다리 역할을 하는 주석 유형들
다양한 기술들이 비지니스 목적을 위해 사용되는 주석들과 aspect 구현에 사용되는 주석들 간 다리로서 사용하고 있다. 예를 들어, @Purchase@OrderProcessing 주석을 가진 모든 메소드들이 트랜잭션 관리가 되어야 한다는 것을 알고 있다면 Listing 11 처럼 aspect를 작성할 수 있다.

Listing 11. 비지니스 주석을 크로스커팅 주석으로 변환하기
public aspect BusinessTransactionBridge {    declare annotation: @Purchase *.*(..): @Transactional;    declare annotation: @OrderProcessing  *.*(..): @Transactional;}

이 aspect는 @Transactional 주석을 @Purchase 또는 @OrderProcessing 주석을 가진 모든 메소드에 붙인다. 이 방식은 Listing 2와 Listing 6을 결합하여, 트랜잭션 관리 로직으로 메소드를 실행한다.

결론

메타데이터는 프로그램 엘리먼트에 대한 추가 정보를 표현하는 하나의 방식이다. 자바의 새로운 메타데이터 장치는 유형화된 주석을 사용하도록 한다. 메타데이터 사용법은 매우 간단하다. 어떤 것을 사용하는 지는 본인의 선택에 달려있다. Aspect 지향 프로그래밍은 메타데이터의 기본적인 소비자이다. 안정적인 서명 기반의 pointcut으로 쉽게 지정되지 않는 크로스커팅 문제에 대해 보다 단순한 pointcut을 사용하여 메타데이터로 증가된 join point 모델은 AOP를 보다 접근 가능한 것으로 만든다.

이 글에서 메타데이터 개념을 설명하고 AOP에서 프로그램 엘리먼트에 대한 메타데이터에 저장된 정보를 사용하는 방법을 설명했다. 또한 다양한 AOP 시스템에 대한 메타데이터 기반의 pointcut을 지원하는데 어떤 방식들이 개입되어 있는지 설명했다. 다섯 가지의 디자인 리팩토링을 통해 AOP 시스템에 메타데이터를 적용하는 방법 또한 제시했다.

다음 글에서는 AOP를 소비자와 공급자로서 메타데이터를 정의하고 사용하는 디자인 고려 사항들을 자세히 설명하겠다. AOP 시스템에서 메타데이터를 결합하는 것이 망각 원리에 어떤 영향을 끼치는 지와 AOP 시스템의 채택에는 어떤 영향이 있는지도 설명하겠다. 다차원 작업 공간에서 신호로서 AOP에 접근하는 고급 방식도 설명할 것이다.

참고자료

AOP@Work.
"Annotations in Tiger, Part 1: 메타데이터를 자바 코드에 추가하기"
• AOP 실행: AspectJAspectWerkzJBoss AOP
Spring AOP
proposed language modifications
"Aspect-Oriented Programming and JBoss
"Aspect-Oriented Annotations"
TestNG , "TestNG로 자바 단위 테스트를 쉽게!"
Barter
Contract4J
"Metadata for language extensions"
AspectJ in Action: Practical Aspect-Oriented Programming
Aspect-Oriented Software Development
Mastering AspectJ: Aspect-Oriented Programming in Java
Eclipse AspectJ
AspectJ Cookbook
Java technology zone
Developer Bookstore,
Java- related titles
Java technology zone tutorials page, developerWorks

제공 : DB포탈사이트 DBguide.net

출처명 : 한국IBM
Posted by tornado
|