달력

11

« 2024/11 »

  • 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


이일민 l http://dbguide.net/comb/common/edit/tobyilee@gmall.com l Epril 대표 컨설던트


AOP의 시대가 도래하고 있다. AOP가 등장한지 10년이 되면서 AOP는 많은 개발자들의 주목을 받고 있다. 하지만 AOP를 본격적으로 적용하는 길은 쉽지만은 않은 탓에 많은 개발자들이 AOP의 문턱을 넘지 못하고 포기하는 경우가 많다. AOP는 잘 쓰면 좋지만 없어도 크게 아쉬울 것 없는 선택 기술일까? 아니면 이제는 반드시 익혀야 하는 필수 기술일까?


객체지향프로그래밍(OOP)은 지금까지 출현한 개발 패러다임 중 가장 성공한 것으로 평가되고 있다. 그 때문에, 현재 시스템 개발에 가장 많이 쓰이는 언어의 대부분은 OOP기반으로 설계된 것들이다. 또, 가장 많이 사용되는 언어인 자바와 C#을 비롯해 오랫동안 사용되어져 온 C++, 스몰토크(Smalltalk) 그리고 최근에 각광받고 있는 다이나믹 언어인 파이썬(Python)과 루비(Ruby)에 이르기까지 모두 객체지향언어라는 것을 장점으로 내세우고 있을 정도다.



AOP의 도입의 필요성



객체지향 프로그래밍의 가장 큰 특징은 시스템이 다루고자 하는 도메인의 모델을 설계와 구현에 손쉽게 반영할 수 있다는 데 있다. 오브젝트 모델자체가 실제 세계의 개체를 투영하고 있기 때문이다. 고객의 요구사항과 도메인을 가지고 객체지향적인 설계를 하고 나면 그 다음 작업은 매우 수월하다. OOP의 경우 설계(design)와 구현(implementation)은 거의 1:1 관계에 있기 때문이다. 그래서 OO툴을 사용하면 설계 자료에서 자동으로 코드를 생성해 낼 수 있기도 하다. 따라서 많은 경우 설계상의 기능은 각각 하나의 클래스 또는 패키지로 구현될 수 있다.


설계와 구현이 정확히 대응되는 이상적인 상황이라면 OOP의 장점이 극대화될 수 있다. 각 패키지별로 독립적인 개발이 가능해 최적화된 형태의 개발 작업이 가능하다. 또 유지보수가 쉬워지고 독립적인 테스트도 수월해진다. 더 나아가 각각의 컴포넌트를 재활용하거나 확장해나가기도 쉽다. OOP나 객체지향분석설계(OOAD)를 설명하고 있는 책에 나오는 모델, 설계다이어그램과 그것을 구현한 코드를 보면 이런 OOP의 매력에 감탄하게 된다. 하지만 현실세계의 OOP 개발자들이 이런 OOP의 장점을 충분히 누리고 있는지 묻는다면 쉽게 그렇다고 대답하기 어렵다.


엔터프라이즈 시스템의 복잡도는 점점 증가하고 있다. 이런 복잡한 시스템은 요구사항과 기능을 모두 충족시키면서 객체지향적인 설계를 하는 것이 매우 어려워진다. 결국에는 객체지향적인 원칙을 지키지 못하고 타협할 수밖에 없는 상황에 이르는 것이 현실이다. OOP의 장점을 살리지 못하게 방해하는 이런 타협의 결과는 크게 두 가지로 나타난다. 하나는 기능의 분산이고 다른 하나는 코드의 혼란이다.



분산(Scattering)



하나의 기능을 하나의 모듈로 캡슐화하기 불가능하거나 힘든 경우가 있다. 도메인의 핵심기능을 객체지향적인 원칙에 따라 설계했다하더라도 많은 엔터프라이즈 서비스 기능들은 단일 모듈로 독립시킬 수 없고 여러 모듈에 분산해서 존재하게 된다. <그림 1>은 이러한 실제 예를 보여주는 그림이다. 객체지향적으로 설계한 모듈들 안에 보안이나 모니터링 기능이 분산되어 존재하고 있다. 이런 기능들은 단일 모듈로 만드는 것이 불가능하다. 따라서 이런 기능의 코드들은 여러 개의 모듈 안에 분산되어 반복적으로 나타나게 된다. 필연적으로 중복된 코드를 양산하게 되는 것이다.


결과적으로 이런 분산된 기능의 코드는 유지보수를 힘들게 한다. 더 나아가서는 객체지향적으로 설계된 모듈임에도 재사용이 어려워 질 수 있다. 분산된 코드에 침범 당한 모듈은 의존성이 매우 강해지기 때문이다.



혼란(Tangling)



여러 개의 모듈에 분산되어 중복되어있어 모듈화가 불가능한 코드들은 각 모듈의 코드자체를 혼란에 빠뜨리게 된다. 아무리 모듈의 오브젝트 설계자체는 단일책임원칙에 따라서 잘 설계했다 할지라도 이렇게 침범한(invasive) 코드들로 인해서 원래의 깔끔한 설계와 코드는 직접적인 연관성의 기능이 뒤엉켜 지저분한 코드가 되어 버린다.


<리스트 1>은 객체지향설계에 따라 설계된 이상적인 OOP코드이다. 하지만 많은 엔터프라이즈 서비스의 기능들이 독립적으로 존재하지 못하고 코드를 침범하면 <리스트 2>와 같이 다양한 기능이 뒤엉킨 지저분한 코드로 바뀌게 된다.


사용자 삽입 이미지

<그림 1> OOP모듈에 분산된 기능


<리스트-1> 침범당하지 않은 OOP코드

class MemberService {
MemberRepository repository;

public void upgradeLevel(List<Member> members, int newLevel) {
for(Member member : members) {
member.changeLevel(newLevel);
repository.updateMember(member);
}
}

}


<리스트-2> 침범당한 OOP코드

class MemberService {
MemberRepository repository;
Log logger;
TranasctionManager txManager;
SecurityService securityService
NotificationPolicy notificationPolicy;
EMailService emailService;

public void upgradeLevel(List<Member> members, int newLevel) {

// Logging
if (logger.isDebug()) { logger.debug("upgradeLevel() started", members, newLevel)
}

// Security
if (!securityService.checkPerssionForModify(Member.class)) {
if (logger.isWarn()) {
logger.warn("Security violation: Member modification", new Date());
}
throw new SecurityException(Member.class, SecurityType.Modification);
}

// Transaction
Transaction tx;
try {
tx = transactionService.beginTransaction();

for(Member member : members) {
member.changeLevel(newLevel);
repository.updateMember(member);
}
}
catch(DataException e) {
try { tx.rollback(); } catch(Exception e2) {throw e2;}
throw new ExceptionTranslater().translateToSystemException(e);
}

if (notificationPolicy.isEMailNotificationEvent("Member.upgradeLevel")) {
emailService.sendNotificationToMemberAdmin(members, newLevel)
}

}

}

OOP를 학습하기 위해서 작성하는 코드가 아니라면 현실 세계 대부분의 코드는 <리스트 2>와 유사하게 만들어진다. 엔터프라이즈 시스템의 복잡도와 요구사항이 날로 높아지는 요즘 같은 경우는 더더욱 복잡한 기능이 결합되어 있는 코드가 만들어지게 된다. 결과적으로 OOP의 장점인 모듈의 독립성이나 재사용, 이해하기 쉬운 코드, 리팩토링, 단위테스트 편이성 등이 극도로 저하된다. <리스트 2>는 OOP언어를 사용해서 작성한 코드이지만 더 이상 OOP의 장점을 찾아보기 힘든 코드가 되어버렸다.


이렇게 독립된 모듈로 분리시킬 수 없는 기능들은 주로 개발하는 시스템의 핵심 로직을 담은 핵심 도메인 로직과는 다른 차원에서 존재하면서 도메인 오브젝트에 분산되어 존재할 수밖에 없는 것들이다. 대표적으로 Tracing, Logging, Error and Exception Handling, Monitoring, Statistics gathering, Transaction, Session Management, Threading, Synchronization, Caching, Remote access 등이다. 이러한 기능은 대부분 엔터프라이즈 서비스나 인프라스트럭처 서비스라고 불리는 것들이고 날이 갈수록 점점 더 많이 요구되어지고 있다.


타협할 것인가 대안을 찾을 것인가?


이러한 문제에 맞닥뜨린 개발자들은 결국 두 가지 중에 하나를 선택해야 한다. 하나는 OOP의 장점을 포기하고 타협하는 것이다. 코드의 중복을 피할 수 없고 지속적인 유지보수, 재활용의 어려움이 있지만 복사&붙이기와 같은 방법을 통해서 일단은 쉽게 접근할 수 있는 <리스트 2>와 같은 방법을 선택하는 것이다.


두 번째 방법은 AOP를 이용해서 이러한 문제들을 풀어나가는 것이다. AOP는 바로 이런 분산, 혼재되어있는 코드의 문제점을 극복하고 <리스트 1>과 같은 OOP의 장점과 특성을 그대로 간직한 코드를 만들 수 있도록 돕기 위해서 존재하는 기술이다.


많은 개발자들은 AOP가 OOP를 대신해서 독립적인 프로그래밍 패러다임으로 존재하는 대체기술로 오해하고 있다. 하지만 AOP는 OOP가 가진 한계를 극복하고 OOP의 원칙에 충실한 설계와 개발이 가능하도록 지원해주는 역할을 하고 있다. 사실 AOP로 만들어낼 수 있는 모든 기능은 OOP로 할 수 있는 것들이다. 하지만 OOP만으로는 객체지향원칙에 타협을 해야 할 수밖에 없기 때문에 AOP를 사용하는 것이 훨씬 유리한 선택일 것이다.


AOP가 어떻게 OOP의 코드를 유지한 채로 이러한 기능들을 적용해낼 수 있는지는 지난 2005년 11월호 <마소>에 필자가 쓴 ‘관점지향프로그래밍-AOP’이라는 기사를 참조하길 바란다.



AOP의 도입의 장애물



그렇다면 이렇게 장점이 많은 AOP가 나온 지 10년이 되도록 폭 넓게 적용되지 못한 이유는 것일까? 그 이유는 AOP가 생각보다 도입하기가 쉽지 않기 때문이다. 많은 개발자들이 AOP에 대해서 관심을 가지고 있음에도 AOP를 실제 업무에 적용하지 못하게 하는 AOP 도입의 장애물은 세 가지 정도로 생각해 볼 수 있다.


AOP에 대한 미신과 오해


첫째는 AOP에 대한 미신과 오해들로 인해서 AOP도입을 꺼리게 되기 때문이다.


AOP를 처음 접하는 개발자들은 아주 간단한 예제로 만들어진 튜토리얼을 접하면서 시작한다. 현실 세계에서 가장 많이 필요로 하고 구현하기 쉽기 때문에 자주 등장하는 예제는 Logging과 Tracing이다. 간단한 크로스컷(crosscut)으로 표현이 가능하고 어드바이스(advice)코드가 단순하기 때문에 AOP 예제에 자주 등장한다. 문제는 이로 인해서 많은 개발자들이 AOP의 활용정도를 단순히 메소드 실행을 추적하거나 로그를 남기는 정도로 한정지어 생각하는 것이다. 그러나 AOP의 활용용도는 매우 다양하다.


다음의 요구사항을 생각해보자.


● 서비스 레이어의 메소드는 트랜잭션 안에서 동작해야하고 서비스 레이어의 경계를 넘어올 때 commit 되거나 예외가 발생했을 때는 rollback되어야 한다.
● 만약 메소드의 이름이 get으로 시작하면 읽기 전용의 트랜잭션이 생성되어야 한다.
● JDBC 코드로 만든 데이터 레이어에서 발생하는 SQLException은 자동으로 시스템의 표준 Exception hierachy의 하나로 변환되어 다시 던져져야 한다.
● 데이터 레이어의 메소드는 반드시 서비스 레이어의 메소드에서만 호출되어야 한다. 프레젠테이션 레이어나 JSP 등에서는 DAO 메소드를 호출할 수 없고 개발자가 강제로 그러한 코드를 만든다면 에러가 발생해야 한다.
● Optimistic-locking에 의해서 실패한 서비스 메소드는 정해진 횟수만큼 반복해서 재시도 되어야 한다.


AOP를 이용하면 이러한 요구사항을 충족하는 기능을 OOP의 핵심코드를 전혀 수정하지 않고 시스템 전체에 걸쳐서 적용하는 것이 가능하다. AOP의 활용용도는 이외에도 무궁무진하다.


AOP는 매우 복잡한 기술이고 결과적으로 전체 프로그램도 복잡해지며 이해하기 힘들어진다는 편견도 문제다. AOP의 구현기술이 복잡한 것은 사실이다. AOP를 사용하기 위해서는 OOP 언어를 확장하거나 별도의 컴파일러가 필요하고 바이트 코드조작 등의 고급기법이 사용된다. 하지만 그것은 AOP 툴을 만드는 개발자의 부담일 뿐이고 그것을 사용하는 개발자는 오히려 더 단순하고 심플한 개발을 할 수 있다. 그 이유는 AOP를 이용하면 훨씬 높은 수준의 추상화가 가능하기 때문이다. 물론 추상화가 잘 될수록 프로그램의 흐름을 이해하는 것은 더 어려워진다. 하지만 그것은 OOP도 마찬가지다. 추상화의 장점이 크기 때문에 절차적 프로그래밍보다 OOP를 선호하는 것처럼 AOP의 추상화도 마찬가지이다. 또 AOP로 추상화를 하지 않은 타협한 OOP코드는 사실 이해하기가 훨씬 더 어렵다.


그 외에도 AOP의 기능은 잘 설계된 인터페이스나, 디자인패턴 등을 잘 활용하면 대체할 수 있다는 것도 잘못된 이해이다. 인터페이스나 디자인패턴은 OOP 자체의 설계를 건전하고 견고하게 만들어줄 수 있다. 하지만 AOP는 OOP로는 해결할 수 없는 부분을 담당하는 것이기 때문에 단지 인터페이스나 디자인패턴이 AOP를 대체할 수 없다.


AOP의 학습에 대한 부담


두 번째 문제는 AOP의 학습에 대한 부담이다.


새로운 기술을 습득하고 더 나아가 새로운 패러다임을 수용하는 것은 언제나 부담스러운 일이다. AOP를 학습하고 사용하려면 절차적 프로그래밍에서 객체지향 프로그래밍으로 전환할 때만큼의 수고가 필요하다.


AOP 학습이 어려운 이유는 OOP와 패러다임 자체의 차이도 있지만, 거기에 다른 새로운 언어적인 요소도 추가됐기 때문이다. 가장 대표적인 AOP 툴인 AspectJ는 자바 언어 자체를 확장해서 만들어진 새로운 언어이다. 따라서 컴파일러도 전용컴파일러를 사용한다.


AspectJ에서 가장 중요하면서 제일 어려운 부분은 포인트컷(Pointcut)이다. <리스트 3>의 포인트컷 선언 같은 경우는 사실 쉽게 이해하기 어렵다. AOP에서 포인트컷은 RDB의 SQL과 유사하다고 생각하면 된다. SQL을 충분히 익히지 않으면 RDB 개발에 어려움이 있는 것처럼 포인트컷을 마스터하지 않고는 AOP를 제대로 활용하기 힘들다. 잘못된 포인트컷의 사용은 AOP를 적절히 활용하지 못하고 애플리케이션을 복잡하게 만드는 등의 부작용을 일으킬 수 있다.


<리스트-3> 복잡한 포인트컷 선언

(execution(!private * com.other..*.set*(*))
|| execution(!private * com.other..*.get*(*)))
&& cflow(execution(public * com.mycompany..*.*(*, *, ..)))


따라서 AOP를 학습할 때에는 포인트컷에 대한 투자를 충분히 해야 한다. 포인트컷의 문법을 숙지하고 다양하게 작성된 포인트컷을 분석해보는 작업이 필요하다. 포인트컷 안에 사용되는 조인포인트(Join Point)의 정의와 의미도 정확히 이해해야한다.


AspectJ 5에서는 Java5의 어노테이션을 이용한 포인트컷이 도입되었다. 와일드카드를 이용한 기존의 포인트컷보다 좀 더 편리한 타깃 설정이 가능하다는 장점이 있지만 반면에 포인트컷 자체를 더 이해하기 어렵게 만들기도 한다. <리스트 4>는 언뜻 보기엔 유사하지만 의미가 다른 어노테이션을 이용한 세 가지 포인트컷의 예이다. 첫 번째 포인트컷은 @Transactional 어노테이션을 가진 모든 종류의 메소드의 실행(execution)을 가리키는 포인트컷이다. 두 번째는@Transactional 어노테이션을 가진 오브젝트를 리턴하는 모든 메소드를 실행하는 것을 정의한 포인트컷이다. 세 번째는@Transactional 어노테이션을 가진 타입(type)에 정의된 모든 메소드의 실행에 대한 포인트컷이다. 매우 짧고 간단한 포인트컷들이지만 이를 명확히 구분하려면 정확한 포인트컷 언어에 대한 정의를 알고 있고, 익숙하게 사용할 수 있도록 학습하는 것이 절실히 필요하다.


<리스트-4> 어노테이션을 이용한 다양한 포인트컷 정의

execution(@Transactional * *.*(..))
execution((@Transactional *) *.*(..))
execution(* (@Transactional *).*(..))

또 아직 이렇다할 표준이 없다는 점도 AOP 학습을 어렵게 하는 이유 중 하나이다. 따라서 AOP 툴에 따라 사용하는 용어가 조금씩 다르고 포인트컷이나 애스펙트를 정의하는 것이 제각각이다. 그로인해 용어의 혼동을 가져오기도 하고 하나의 AOP 툴을 충분히 이해했다하더라도 다른 AOP 솔루션을 사용하면, 또다시 새로운 학습이 필요하게 되는 어려움이 있다. AOP의 표준을 만들거나 스펙의 연합체를 만들려는 노력은 계속 있어왔다. 대표적으로 SpringAOP가 참여한 AOP Alliance는 Java/JEE의 AOP 표준을 만들기 위한 시도 중의 하나였다. 하지만 여전히 명확히 정의되고 모든 AOP 솔루션이 따르고 있는 표준은 존재하지 않는다. 그나마 다행스러운 것은 AOP 툴 사이에 여러 가지 호환을 위한 노력이 진행되고 있다는 점이다. AspectJ의 포인트컷 언어를 사용하는 Spring2.0 AOP의 예가 그런 긍정적인 노력의 하나라고 할 수 있다.


AOP의 학습이 쉽지 않은 것은 사실이지만 그 필요성을 절실히 인식하고 기술습득에 대한 충분한 투자를 할 만한 가치를 인정하는 것이 필요하다.


AOP도입전략의 부재


세 번째는 적절한 AOP 도입전략을 가지고 있지 못하기 때문이다.


AOP를 도입하려는 개발자나 기업의 가장 잘못된 접근방법은 단번에 AOP를 도입하려는 욕심을 내는 데 있다. AOP를 전부 아니면 전무(All-or-nothing) 개념으로 접근하는 것은 바람직하지 않다. AOP는 패러다임의 전환이고 상당한 노력과 시간이 필요하다. 또 그 적용은 팀 내 모든 개발자에게 영향을 미친다. 섣부른 AOP의 도움은 오히려 AOP에 대한 실망만 안겨주고 그다지 좋은 결과를 얻지 못할 수 있다.


AOP 도입의 가장 이상적인 방법은 단계적인 접근이다. 처음에는 가볍고 쉽게 시작할 수 있는 것부터 출발해서 지속적인 학습과 검증을 병행해 가면서 난이도가 높은 단계로 발전해나가는 것이다. AOP는 OOP 전체가 아닌 일부를 대체하기 때문에 그 정도를 조절하는 것이 가능하다.


사용자 삽입 이미지

<그림 2> AOP의 단계적인 도입 전략


<그림 2>는 AOP 전문가들이 추천하는 AOP 도입전략이다. 크게 4단계로 구분해서 1단계부터 단계적으로 적용하고 성공적인 적용이 끝나면 다음 단계로 나아가는 식이다.


첫 번째 단계는 프레임워크 등을 통해서 이미 구현되어있는 애스펙트를 사용하는 것이다. 가장 대표적인 것으로 SpringAOP를 이용한 서비스 레이어에 대한 트랜잭션 애스팩트의 적용이 있다. 스프링프레임워크를 사용하는 개발자는 AOP에 대한 지식 없이 간단한 설정만으로 AOP의 혜택을 누릴 수 있다. 사실 대부분의 스프링 사용자는 SpringAOP를 직접 사용하지 않는다. 하지만 프레임워크에서 제공하는 AOP 기능을 충분히 누릴 수 있다. 이처럼 손쉽게 쓸 수 있는 애스펙트를 적용하면서 AOP로 인해서 단순해진 핵심로직의 편리함을 누리는 것이 AOP의 매력을 느끼고 다음단계로 나아갈 수 있는 출발이 될 수 있다.


두 번째 단계는 정책의 검사와 강제(exploration and enforcement)에 AOP를 적용하는 것이다. 시스템 전반에 걸친 정책을 일괄적으로 적용하는 것은 쉬운 일이 아니다. 모든 개발자들이 정책을 명확히 인지하고 있어야 하고 그것을 위반하지 않고 개발에 적용해야 하기 때문이다. 대형프로젝트에는 수십 페이지의 정책문서가 작성되고 개발자들에게 공개된다. 개발자들은 그 규정에 따라 코드를 작성해야 한다. 그렇게 만들어진 코드에 정확히 정책이 적용되었는지를 검증하는 것 또한 큰일이다. 품질팀의 사람들이 여러 단계에 걸쳐서 그것을 검토하지만 끊임없이 수정되고 발전하는 코드를 지속적으로 체크하는 것은 어려운 일이다. AOP는 이런 전체적인 정책을 검사하고 필요에 따라 강제할 수 있는 효과적인 수단을 제공한다. 이 단계에서는 아직 모든 개발자가 AOP에 대한 많은 지식을 가질 필요도 없다. 정책을 결정하고 부여하는 역할을 담당하는 소수의 사람에 의해서 적용이 된다. 더 나아가서 필요하면 정책의 변경도 손쉽게 할 수 있다.


레이어기반의 아키텍처(Layered Architecture)에서 레이어 간의 의존성과 메소드의 호출 정책은 중요한 요소이다. 얼마 전에 필자는 대형프로젝트의 아키텍트로부터 개발자들이 이 정책을 지키지 않고 편의에 따라서 뷰(JSP)에서 데이터 레이어(DAO)의 인스턴스를 만들어서 사용하고 있는 것 때문에 고민이라는 얘기를 들었다. 시스템 레벨에서 일괄적으로 이것을 막을 수 있는 방법에 대한 조언을 구했기에 간단히 AOP를 적용하도록 조언해줬다.
AOP를 이용하지 않고 이런 적용을 하려면 매우 복잡할 것이다. 모든 DAO 메소드에 현재 쓰레드의 호출스택(Call Stack)을 조사해서 지정된 레이어가 아닌 곳에서 호출이 있다면 이를 막는 코드를 다 삽입해야 할 것이다. 큰 프로젝트라면 수백, 수천 개의 메소드에 이 코드를 모두 삽입해야 한다. 하지만 AOP를 적용하면 간단히 이런 정책을 강제할 수 있다. <리스트 5>의 AOP 코드만으로 어떤 뷰에서도 DAO를 사용할 수 없도록 제한 할 수 있다. 더 나아가서 정책이 변경된다면 언제든지 이 애스펙트를 수정하는 것만으로 전체 시스템에 정책의 변경을 적용할 수 있다.


<리스트 5> 레이어간 호출 정책 애스펙트

aspect LayerCallingPolicy {
pointcut inView() : within(view..*);
pointcut daoCall(): call(* dao..*(..)) &&!inDao();

declare error : daoCall() && inView(): "View에서 DAO를 직접 호출할 수 없습니다";

}

try/catch를 사용한 모든 코드에서 catch 후 아무 것도 하지 않는 dummy catch를 찾아내는 것을 생각해보자. 코드리뷰를 통해서 하려면 엄청난 작업이다. 반면에 AOP를 이용하면 세 줄 정도의 코드만 추가하면 충분하다.


세 번째 단계는 인프라스트럭처 애스펙트의 적용이다. 인프라스트럭처 서비스를 사용하는 것은 개발자들이 항상 해야 하는 일이기 때문에 이 단계에서 AOP를 적용하는 것은 팀 내 모든 개발자들이 분명하게 인지하고 있어야 한다. 대부분의 인프라스트럭처 서비스나 엔터프라이즈 서비스를 이 단계에서 AOP를 통해서 모듈화 할 수 있고 개발자들은 결과적으로 OOP에 충실하게 핵심로직을 개발하는 것이 가능해진다. 로깅, 에러핸들링, 모니터링, 트랜잭션, 세션관리, 캐슁 등의 많은 서비스들을 AOP에 적용할 수 있다.


세 번째 단계가 되면 시스템 전체적으로 AOP가 중요한 역할을 담당하게 된다. 이쯤 되면 본격적으로 AOP를 사용하고 있다고 생각할 수가 있다. 어느 정도 사용패턴이 정해져 있기 때문에 전략을 잘 세우고 소수의 AOP 개발자들만 수고하면 나머지 개발자들은 매우 편리하게 OOP에 집중해서 핵심로직을 개발할 수 있을 것이다.


마지막 네 번째 단계는 각 도메인에 특화된 AOP를 적용하는 단계이다. 이때부터는 개발자들이 매우 깊게 AOP를 사용하게 된다. 도메인이나 시스템에 특화된 로직의 많은 부분에 애스펙트를 적용할 수 있기 때문이다. 이때는Introduction(Inter-type declaration)이나 Mixin같은 고급기법을 이용할 수도 있다. 여기서 AOP와 OOP가 교차하는 부분이 생긴다. OOP의 추상화를 이용한 방법이 가능하지만 그것이 시스템의 설계를 복잡하게 한다면 AOP를 이용하는 것도 고려해볼만 하다. 예를 들어 OO디자인 패턴의 대표적인 옵저버패턴(Observer Pattern)은 객체지향적인 패턴을 활용해서 OOP로 구현할 수 있지만 경우에 따라서는 AOP를 사용하면 매우 간단하게 구현할 수도 있다.


단계적인 AOP 적용과 학습을 통해서 전체 팀이 AOP에 익숙해진 시점에서 이 네 번째 단계를 적용하기 시작하면 된다. AOP의 성공적인 적용에는 반드시 단계가 있다. 충분한 이해와 경험이 없이 처음부터 모든 영역에 AOP를 사용하려고 하는 것은 반드시 문제를 일으키게 마련이다.



AOP의 미래



필자가 AOP에 대한 기사를 처음 썼던 때에 비하면 지금은 AOP가 훨씬 많이 사용되고 있다. 특히 스프링프레임워크는 AOP를 널리 알리고 손쉽게 실무에 적용하게 해준 일등공신이다. 또 AspectWertz 프로젝트와 통합되고 Java5의 많은 특성을 적용한 AspectJ 5의 발전은 AOP 도입의 견인차 역할을 톡톡히 하고 있다. 최근엔 AspectJ와 SpringAOP도 긴밀하게 연동이 되어서 AOP 툴 간의 비호환 문제가 많이 해소되었다.


AOP를 이용하기 시작한 개발자들의 공통적인 반응은 “AOP가 없을 때는 어떻게 개발을 했는지 모르겠다”고 할 정도로 매우 뜨겁다. 최근에 등장한 많은 기술들의 공통점은 개발자들이 기술적인 것보다는 도메인의 비즈니스로직에 더 집중할 수 있도록 하는 것과 객체지향의 기본정신에 충실할 수 있도록 돕는 것이다. POJO 기반의 개발전략과 기술이 바로 그런 것들이다. 동시에 POJO 중심의 개발에서 AOP는 빠질 수 없는 필수 도구이다.


점점 복잡해지는 시스템의 요구사항을 기술적인 타협을 통해서 적당히 해결하고 후에 많은 문제를 떠안고 갈 것인가 아니면 처음엔 조금 부담이 되지만 AOP라는 멋진 대안을 선택해서 개발의 즐거움을 누릴 것인가 하는 것은 이제 개발자들의 선택에 달려있다.


참고자료

1) Aspect In Action, Ramnivas Laddad, Manning(2003)
2) One-on-one J2EE Development without EJB, Rod Johnson, Wrox(2004)
3) The AspectJ Project, http://www.eclipse.org/aspectj/
4) Spring 2.0 reference manual, http://static.springframework.org/spring/docs/2.0.x/reference/index.html
5) AOP@Work: AOP myth and realities, http://www-128.ibm.com/developerworks/java/library/j-aopwork15/
6) Introduction to Practical AspectJ Programming, SpringOne 2006
7) AOP in the enterprise, SpringOne 2006
8) Avoing AOP pitfalls, The Spring Experience 2006
9) AsoectJ for Spring Developer, The Spring Experience 2006

출처명 : 한국마이크로소프트 [2007년 3월호]
:
Posted by 뽀기