HomeBlogGuestbookLab 

JDM's Blog

온갖 테스트 결과가 기록되는 이곳은 JDM's Blog입니다. :3

관점 지향 프로그래밍(Aspect-oriented programming)

Spring 프레임워크가 생긴 이후에 처음으로 AOP(Aspect-oriented programming)라는 단어를 접했었습니다. -_- 무식이 죄지요. 이번에는 AOP를 철저하게 해부해보고자 합니다. 이 포스팅은 전부 Wikipedia - Aspect-oriented programming에서 가져온 정보를 토대로 작성했습니다.

관점 지향 프로그래밍(Aspect-oriented programming)

전산학에서 관점 지향 프로그래밍(AOP)은 횡단 분할 기법(separation of cross-cutting concerns)을 사용함으로써 모듈러리티(modularity)를 증가시키는 것에 촛점을 맞춘 프로그래밍 패러다임입니다. AOP는 관점 지향 소프트웨어 개발을 위한 기초 형태입니다.

AOP는 프로그래밍 메소드와 도구들을 포함합니다. 이것들은 전체 엔지니어링 분야에서 "관점 지향 소프트웨어 개발(aspect-oriented software development)"를 참조하는 동안 소스 코드 레벨 단에서의 모듈화를 지원합니다.

관점 지향 프로그래밍은 프로그램 로직을 분리된 파트로 변화시키는 것을 의미합니다. 거의 모든 프로그래밍 패러다임은 추상화를 제공(예를 들자면 함수, 프로시저, 모듈, 클래스, 메소드)합니다. 그리고 이것을 구현 하거나 추상화 및 복합적으로 사용해서 분리 및 독립적인 엔티티들을 고려해 그룹화와 캔슐화하는 것을 일부 레벨 지원합니다. 고려할 것이 약간 있는데 "컷 크로스(cut cross)"가 있습니다. 컷 크로스는 프로그램의 구현 양식을 무시하면서 복합적인 중첩을 말합니다. 이것을 "크로스 커팅 관계(cross-cutting concerns)"라고 합니다.

로깅(Logging)은 로깅 전략(반드시 시스템의 모든 로깅된 파트의 영향을 준다.) 때문에 횡분할 관계를 주시합니다. 로깅은 모든 로깅된 클래스와 메소드들 근처에서 크로스 컷(crosscuts)합니다.

모든 AOP 구현(implementations)은 한 장소에서 각각의 관계를 캡슐화하는 약간의 횡분할 표현식(crosscutting expressions)을 가집니다. 구현마다 제공된 구조의 성능, 안정성 그리고 유용성이 다릅니다. 예를 들자면, 차단하기 위한 특별한 메소드들인 인터셉터(interceptors)는 타입 안정성(type-safety)과 디버깅을 위한 많은 지원 없이 크로스커팅의 제한된 양식을 표현합니다.

AspectJ는 다수의 표현식과 aspect라는 특별한 클래스를 캡슐화하는 것을 가집니다. 예를 들면, aspect는 pointcut(join point와 매칭이 되는지 검출하는)이라 불리는 특별한 쿼리와 여러 join points(프로그램의 특정 지점) 상에서 advice(추가적인 행위)를 지원함으로써 기반 코드(프로그램의 aspect 파트가 아닌 부분)의 행위를 변경할 수 있습니다. aspect는 또한 멤버 또는 부모를 추가하는 것처럼 바이너리에 적합한 구조로 다른 클래스의 변경을 할 수 있습니다.

동기 및 기본 컨셉(Motivation and basic concepts)

전형적인 aspect는 흩어져 있거나 얽혀 있고 이해와 유지보수하기가 어렵게 만들어진 코드입니다. 이것은 전체적으로 연관이 없는 시스템, 다른 프로그래밍 언어, 기타등의 것이라도 연관이 없는 함수들로 인해 아주 넓게 확산됩니다. 이 말의 뜻은 로깅을 변경하고자 할 때 영향이 끼치는 전체 모듈들을 수정해야 한다는 것입니다. Aspect들은 시스템의 메인뿐만 아니라 다른 각각에 표현된 것에도 얽혀지게 됩니다. 이것이 뜻하는 바는 한가지 관계된 것을 바꿀 때 다른 모든 얽혀져 있는 관계까지도 이해해야 합니다.

예를 들면, 은행 업무 어플리케이션에서 한 명이 다른 한명에게 계좌 이체를 하는 경우를 살펴 봅시다.

void transfer(Account fromAcc, Account toAcc, int amount) throws Exception {
 if (fromAcc.getBalance() < amount)
     throw new InsufficientFundsException();

 fromAcc.withdraw(amount);
 toAcc.deposit(amount);
}

그러나, 이 전송 메소드는 어플리케이션을 배포할 때 반드시 고려해야 합니다. 이것은 현재 유저 이 동작을 수행하기 위해 인증이 유효한지 보안을 검증해야 합니다. 데이터베이스 트랜잭션은 우발적인 데이터 손실을 예방하기 위해 동작을 캡슐화하고 시스템 로그, 또는 기타등등에 남겨야 합니다.

아래는 이러한 결점을 보완한 코드입니다.

void transfer(Account fromAcc, Account toAcc, int amount, User user,
   Logger logger) throws Exception {
 logger.info("Transferring money…");

 if (!isUserAuthorised(user, fromAcc)) {
   logger.info("User has no permission.");
   throw new UnauthorisedUserException();
 }

 if (fromAcc.getBalance() < amount) {
   logger.info("Insufficient funds.");
   throw new InsufficientFundsException();
 }

 fromAcc.withdraw(amount);
 toAcc.deposit(amount);

 database.commitChanges();  // Atomic operation.

 logger.info("Transaction successful.");
}

이 예제는 기본 함수적인 것들로 얽혀지게 됩니다. 때때로 비니지스 로직 관심사라 불리기도 합니다. 트랜잭션, 보안, 그리고 로깅 모든것들은 횡분할 관계 예시입니다.

지금부터 만약 우리가 갑자기 안전성을 고려해서 어플리케이션을 수정한다고 가정을 해봅시다. 프로그램의 현재 버전에서 보안 관련 동작을 추가 하는겁니다. 그렇게 한다면 분명히 많은 메소드들에 엄청난 영향을 끼치게 됩니다.

AOP는 이러한 문제를 해결하기 위해 시도합니다. 프로그래머에게 aspects라 불리는 stand-alone 모듈 내의 크로스 커팅 관계를 표현하도록 허용함으로써 말이죠. Aspects는 advice(프로그램내에서 특별한 지점에 있는 코드) 및 inter-type delarations(구조적인 멤버를 다른 클래스에 추가)을 가질 수 있습니다. 예를 들면, 보안 모듈은 은행 계좌에 접근하기 전에 보안 검증을 허용하는 advice를 포함할 수 있습니다. pointcut은 은행 계좌에 접근할 때 시기(join points)를 정의합니다. 그리고 advice 몸체에 어떻게 보안 검증이 구현되었는지 정의합니다. 이 방법은 검증과 장소를 한 곳으로 유지할 수 있습니다. 그리고, 좋은 pointcut은 추후 프로그램 변화를 에측할 수 있습니다. 그래서 만약 다른 개발자가 새로운 메소드를 만들었다면 advice는 새로운 메소드에서도 그것이 실행 될때 지원합니다.

다음은 aspect 로깅을 포함한 예제입니다.

aspect Logger {
 void Bank.transfer(Account fromAcc, Account toAcc, int amount, User user, Logger logger)  {
   logger.info("Transferring money…");
 }

 void Bank.getMoneyBack(User user, int transactionId, Logger logger)  {
   logger.info("User requested money back.");
 }

 // Other crosscutting code.
}

하나는 디버깅 도구 또는 유저 레벨의 도구로써 AOP를 생각 할 수 있습니다. Advice는 당신이 함수 변경을 하거나 또는 배포된 코드가 변경되기 원하지 않는 경우들을 위하여 제공되어야 합니다.

조인 포인트 모델(Join point models)

관점 지향 언어의 어드바이스 관련(advice-releate) 컴포넌트는 조인 포인트 모델(join point model, 약어 JPM)입니다.

JPM은 다음 3가지를 정의합니다.

advice가 실행 가능할 때, 그들은 joint points라 불립니다. 그들은 구동중인 프로그램에서의 추가적인 행위를 유용하게 결합하는 포인트이기 때문이죠. 조인 포인트는 일반적인 프로그래머로써 유용하게끔 주소적이고 이해가능하도록 필요합니다. 이것은 또한 안정적이어야 합니다. 많은 AOP 구현은 조인 포인트로써 메소드 실행과 필드 참조를 지원합니다.

조인 포인트로 지정하는 방법은 포인트컷(pointcut)이라고 불립니다. 포인트컷은 조인 포인트가 일치하는지 여부를 결정합니다. 대부분 유용한 포인트컷 언어들은 기본 언어(예를 들어 AspectJ는 자바 시그니처를 사용) 같은 문법을 사용하거나 이름 명명과 결합을 통해 재사용을 허용합니다.

지정이라는 뜻은 조인 포인트로써 실행하는 것을 약속합니다. AspectJ에서는 advice라 부릅니다. 그리고 이것은 전(before), 이후(after), 전반적인(around) 조인 포인트 실행을 할 수 있습니다. 몇몇의 구현은 또한 다른 클래스 상의 aspect 안의 메소드를 정의하는 것으로 지원합니다.

조인 포인트 모델은 어떻게 조인 포인트가 지정되는지, 조인 포인트에서 허용된 동작, 그리고 표현 가능한 구조적인 개선등으로 노출된 조인 포인트에 기반하여 비교를 할 수 있습니다.

AspectJ's join-point model

해당 글의 전문은 AspectJ를 참조하세요.

AspectJ에서 조인 포인트는 메소드 또는 생성자 또는 표현식, 클래스 & 객체의 초기화, 필드 읽기와 쓰기 접근, 핸들러 예외, 기타를 포함합니다. 그들은 반복(loop), spuer 메소드 호출(super calls), 예외 패스(throws clauses), 중첩 구문(multiple statements), 기타를 포함하지 않습니다.

포인트컷은 primitive pointcut designators(PCDs)의 조합으로써 정의됩니다. "친절한" PCDs는 각각 조인 포인트(e.g, method execution - 메소드 표현식)의 종류를 맞춰봅니다. 그리고 입력은 자바 언어 같은 시그니처를 사용하는 경향이 있습니다. 이러한 포인트컷의 예를 들면 아래와 같습니다.

execution(* set*(*))

이 포인트컷은 메소드-실행 조인 포인트(method-execution join point)를 맞춰봅니다. 만약 메소드명이 "set"으로 시작하면서 반드시 어떠한 타입 인자라도 하나가 있으면이라는 뜻입니다. "유동적인(Dynamic)" PCDs에서는 실행중(runtime) 타입(type)과 변수 바인드(bind)를 검증합니다. 예를 들자면:

this(Point)

이 포인트컷은 현재 실행중인 오브젝트가 Point 클래스의 인스턴스일 때 맞춰봅니다. 기억하세요. 클래스의 규정되지 않는 이름은 자바에서 일반 타입으로 조회 할 수도 있습니다. "범위(Scope)" PCDs는 조인 포인트의 어휘 범위를 제한합니다. 예를 들자면:

within(com.company.*)

이 포인트컷은 "com.company" 패키지의 모든 타입에 대한 모든 조인 포인트를 맞춰봅니다. 여기서 "*"이 뜻하는것은 와일드카드 형태입니다. 어떠한 것이라도 맞춰볼 수 있게 해줍니다. 포인트컷은 재사용을 위해 복합적 또는 이름 기반으로 사용할 수 있습니다. 예를 들자면:

pointcut set() : execution(* set*(*) ) && this(Point) && within(com.company.*);

이 포인트컷은 만약 메소드 이름이 "set"으로 시작하고 this 오브젝트가 com.company패키지의 Point 타입의 인스턴스면 메소드-실행 조인 포인트를 맞춰봅니다. 이것은 "set()" 이름으로 사용하는 것을 참조할 수 있습니다.

Advice는 조인 포인트(before, after, around)에서 포함한 코드를 실행하기 위해 정의합니다. AOP 런타임은 포인트컷이 조인 포인트에 맞춰지면 Advice를 자동적으로 발생시킵니다. 예를 들자면:

after() : set() {
  Display.update();
}

이것은 특별하게 정의합니다 : 만약 Display.update() 코드를 조인 포인트 완료 이후에 실행하라.

Other potential join point models

JPMs의 다른 종류도 있습니다. 모든 advice 언어들은 그들의 JPM 형태로 정의 될 수 있습니다. UML을 위한 가상 aspect 언어(hypothetical aspect language)는 아마도 JPM을 따릅니다 :

  • 조인 포인트는 모든 모델 요소다.
  • 포인트컷은 모델 요소를 바인딩하는 몇몇의 boolean 표현식이다.
  • 포인트 영향의 뜻은 모든 매치된 조인 포인트의 시각화다.
Inter-type declarations

Inter-type declarations은 아마도 AspectJ에서 다음처럼 보입니다 :

aspect DisplayUpdate {
	void Point.acceptVisitor(Visitor v) {
		v.visit(this);
	}
	// other crosscutting code...
}

이 단편적인 코드는 Point 클래스에 acceptVisitor 메소드를 추가하는 것을 보여줍니다.

이것은 어떠한 구조적인 추가라도 본래의 클래스에 적합하도록 요구합니다. 그리고 기존 클래스의 클라이언트들은 동작하기 위해 끊기지 않아야 합니다. AOP 구현을 하는 한 어떤 시간에서라도 모든 클라이언트의 제어를 기대할 수 있습니다.

구현(Implementation)

AOP 프로그램은 두가지 다른 방법으로 다른 프로그램에게 영향을 끼칠 수 있습니다. 밑에 있는 언어와 환경에 의존하면서 :

  1. 결합된 프로그램이 제공된다. 본래 프로그래밍 언어의 유효성과 본래의 프로그램으로부터 궁극적인 인터프리터까지 구별되지 않는 것.
  2. 궁극적인 인터프리터 또는 환경은 구현된 AOP 특징과 이해로 업데이트 됩니다.

변화하는 환경의 가장 구현하기 어려운 것은 대부분 위빙(weaving - 프로그램 변환의 특별한 경우)으로써 알려진 프로세스를 통하여 적합한 결합 프로그램을 생성하는 것입니다. aspect weaver는 관점 지향 코드를 읽고 aspect가 통합된 적당한 객체 지향 코드를 생성합니다. 같은 AOP 언어는 위빙 메소드(weaving methods)의 다양성을 통해 구현될 수 있습니다. 그리고 언어의 시맨틱(semantics)은 위빙 구현의 형태로 이해 되서는 안됩니다. 단지 구현의 속도와 개발의 편의는 결합이 사용된 메소드에 의해 영향을 받게 됩니다.

시스템은 전처리기(preprocessors)를 사용해 소스 레벨 위빙(source-level weaving)을 구현할 수 있습니다. 그러나 자바의 잘 정의된 바이너리 폼은 바이트코드 위버(bytecode weavers)를 활성화합니다. 바이트코드 위버는 .class 파일 형태의 어떠한 자바 프로그램에서도 동작합니다. 바이트코드 위버는 빌드 프로세스 동안 또는 만약 위브 모델(weave model)이 per-class거나, 클래스 로딩 중에 배포 될 수 있습니다. AspectJ는 소스 레벨 위빙을 2001년부터 시작했습니다. per-class 바이트코드 위버는 2002년도부터 시작했고 2005년 AspectWerkz의 통합 이후 향상된 적재 시간(load-time) 지원을 제공합니다.

런타임에 프로그램을 결합하는 어떠한 솔루션은 시각들을 제공합니다. 이 시각이란 프로그래머의 분리된 모델을 유지하기 위해 적당하게 분리된것입니다. 다중 소스 파일을 위한 자바 바이트코드 지원은 적절하게 짠 소스 에디터의 .class 파일을 통해서 차근차근 진행하도록 디버거를 활성화 합니다. 그러나 몇몇 서드 파티 디컴파일러는 이러한 짠 코드를 처리할 수 없습니다. 왜냐하면 그들은 Javac로써 제공된 코드를 기대하기 때문입니다.

배포 시각 위빙(Deploy-time weaving)은 다른 접근을 제공합니다. 이것은 기본적으로 후처리를 의미합니다, 그러나 생성 코드를 덧붙이기보다는 이 위빙은 기존 클래스의 서브 클래스로 접근합니다. 기존 클래스 수정은 메소드 오버라이딩(method-overriding)으로 처리합니다. 기존 클래스는 건들지 않는 것을 유지합니다. 심지어 런타임 중에서나 모든 존재하는 도구들(디버거, 프로파일러, 기타)은 개발하는 동안 사용할 수 있습니다. 이와 유사한 접근은 이미 IBM사의 WebSphere 같은 Java EE 어플리케이션 서버등으로 많이 제공되어 있습니다.

용어(Terminology)

관점 지향 프로그래밍에서의 표준 용어는 다음과 같습니다.

  • Cross-cutting concerns 객체 지향 모델에서 대부분의 클래스는 하나의 특정 기능을 수행하더라도 그들은 종종 공통적이거나 다른 클래스의 보조 요구 사항을 공유합니다. 예를 들어, 스레드가 들어가거나 메소드가 종료할때마다 우리가 데이터 액세스 레이어와 UI 레이어의 클래스에 로깅을 추가할 수 있습니다. 그리고 이러한 관계를 접근 제어(access control)나 정보 흐름 제어(information flow control)같은 보안쪽으로도 연계할 수 있습니다. 심지어 매우 다른 함수를 가지는 클래스라도 코드는 종종 동일한 보조 함수 실행을 필요로 합니다.
  • Join Point advice가 실행 가능할 때, 그들은 joint points라 불립니다.
  • Advice 이것은 당신이 당신의 기존 모델에 지원하기 원하는 추가적인 코드입니다. 포인트컷에 의해 조인 포인트가 실행 된다면 실제 구현체로써 코드가 실행됩니다.
  • Pointcut 이것은 크로스 커팅 관계(Cross-cutting concerns)가 적용될 필요가 있는 어플리케이션 내의 실행의 시점을 나타내는 용어입니다. 조인 포인트를 지정하고 어드바이스(advice)를 실행하는데 사용합니다.
  • Aspect 포인트컷(pointcut)과 어드바이스(advice)의 결합을 aspect라는 용어로 정의합니다. 예를 들면, 우리가 포인트컷과 정확한 어드바이스를 정의함으로써 우리의 어플리케이션에 logging aspect를 추가합니다.
  • Weaving 위빙은 포인트컷에 의해 지정된 어드바이스를 기존 코드에 덧붙이거나 특별한 처리을 하는 것을 말합니다.

다른 프로그래밍 패러다임과 비교(Comparison to other programming paradigms)

Aspect는 객체 지향 프로그래밍과 전산적 리플렉션으로부터 등장했습니다. AOP 언어는 함수적으로 유사함을 가집니다. 그러나 메타오브젝트 프로토콜보다도 엄격합니다. Aspect는 서브젝트(subjects), 믹스인(mixins), 위임(delegation) 프로그래밍 컨셉과 긴밀하게 관련합니다. 관점 지향 프로그래밍 패러다임을 사용하는 다른 방법은 복합 필터와 하이퍼슬라이스(hyperslices)한 접근을 포함합니다. 1970년대 이후로 개발자들은 AOP에 대한 구현 메소드들의 일부를 닮은 interception 및 dispatch-patching 형태를 사용해 왔습니다. 하지만 이것은 의미적으로 한 곳에 쓰여져 제공하는 크로스커팅 명세가 아니었습니다.

디자이너들은 C#의 부분적 타입(partial types)같은 코드의 분할을 달성하는 대체 방안을 고려했습니다. 그러나 이러한 접근은 개별로 하나의 선언적인 구문 코드로 조인 포인트에 도달하는 것을 허용하는 양적인 매커니즘이 부족했습니다.

이것은 연관 되어 보이지 않아보였지만, 테스트에서, 스텁(stubs) 또는 목(mocks)의 사용은 AOP 기술을 요구했습니다. around advice 같은것 말이죠. 여기에 횡분할 고려를 목적으로하는 테스트로써 협력하는 객체들이 있습니다. 이렇게 다양한 목 오브젝트(Mock Object) 프레임워크는 다양한 특징을 제공합니다. 예를 들면 프로세스는 서비스를 계좌 잔액을 얻기 위해 발동합니다. 프로세스의 테스트는 잔액은 덜 중요하고 단지 요청된것에 따르는 계좌를 사용하는 프로세스가 중요합니다.

Closing Remarks

머리가 아픕니다. (...) 개발(중의적 표현) 번역. ㅜㅜ

결론은, pointcut으로 joint point를 지정하고 매칭이 되면 advice를 실행한다는 거군요. 그리고 advice와 pointcut을 합친 것이 aspect구요. 이 한 줄 이해하기 위해 저 수많은 걸... 당분간 전체 번역은 안해야겠어요.