전 세계 자바 EE 개발 프로젝트의 80%가 원래 계획했던 목표를 다 이루지 못하고 실패로 끝나고 만다는 사실은 한편으로는 충격적이지만 어쩌면 당연한 결과라고 받아들여지고 있다. 개발자들은 좀 더 단순한 방법을 찾아야하고 그에 따라 더욱 효과적으로 명확한 결과를 얻어낼 수 있어야 한다. 소프트웨어 개발의 단순함의 핵심에 바로 관점지향 프로그래밍, AOP가 존재한다.
최근 몇 년 간 자바 개발자들이 경험하고 있는 새로운 기술의 흐름과 방향을 한마디로 말해보자면‘단순함(simplicity)의 추구’라고 할 수 있다. 아무리 정교하고 잘 설계된 프로그래밍 기술과 개발 방법론, 분석설계 기술을 사용한다고 하더라도 개발자들이 이해하고 구현해야 하는 애플리케이션의 구조와 구현 내용은 갈수록 복잡하고 다차원적이 되어가고 이를 감당하기 위해서 개발자들은 점점 복잡한 미로 속으로 빠져들어 갈 수밖에 없다.
관점지향 프로그래밍(Aspect Oriented Programming, 이하 AOP)이라는 말을 처음 들어보면 객체지향 프로그래밍(Object Oriented Programming, 이하 OOP)이 떠오른다. AOP는 결국 OOP의 뒤를 잇는 또 하나의 프로그래밍 언어 구조라고 생각될 수 있다. 지금까지 프로그래밍 기술은 기계어와 같은 초기 언어를 기반으로 한 프로그래밍으로부터 출발해서 절차적 프로그래밍을 거쳐 OOP에 이르렀다. 20여년 이상 현장에서의 적용과 내부적인 개념의 발전 그리고 자바나 C# 같은 대중적인 언어의 등장에 힘입어 이제는 OOP가 전체 소프트웨어 개발의 대표적인 패러다임으로 자리 잡고 있다.
그럼 이제 AOP의 등장으로 OOP는 사라져 갈 것이고, AOP가 OOP를 대체하게 될 것이라고 할 수 있을까? 결코 그렇지 않다. AOP와 관련된 가장 중요한 개념은‘AOP는 결코 OOP의 자리를 대신하기 위해서 등장한 기술이 아니다’라는 것이다. 오히려 AOP는 OOP를 더욱 OOP답게 만들어줄 수 있다고 할 수 있다. 많은 AOP 기술의 주도자들과 개발자들은 AOP가 OOP를 사라지게 하는 것이 아니라 더욱 완전하게 만들어 줄 수 있다고 주장한다. 또 AOP는 OOP뿐만 아니라 기존의 절차적 프로그래밍에도 적용되어질 수도 있다. 결국 AOP는 지금까지의 프로그래밍 기술 변화의 흐름에 다른 차원의 관점을 제시함으로써 새로운 프로그래밍 패러다임을 이끌어내고 있다고 볼 수 있다.
AOP의 필요성
AOP의 필요성을 이해하는 가장 기초가 되는 개념은‘관심의 분리(Separation of Concerns)’이다. 관심의 분리는 컴퓨터 프로그래밍의 가장 기초가 되는 원리 중 하나이다. 거의 모든 프로그래밍 패러다임은 바로 이 관심의 분리 과정을 통해 문제 영역(problem domain)을 독립적인 모듈로 분해한다. 절차적 프로그래밍에서는 분리된 관심을 프로시저로 구성하고 OOP에서는 이를 클래스로 작성한다. 여기서 AOP는 OOP를 적용한다고 할지라도 충분히 분리해 낼 수 없는 부분이 있다는 문제 제기에서 출발한다.
AOP에서 주목하고 있는 부분은 OOP와 같은 모듈화가 뛰어난 방법을 사용하더라도 결코 쉽게 분리된 모듈로 작성하기 힘든 요구사항이 실제 애플리케이션 설계와 개발에서 자주 발견된다는 점이다. AOP에서는 이를 횡단 관심(crosscutting concerns)이라고 한다. 이에 대비해서 해당 시스템의 핵심 가치와 목적이 그대로 드러난 관심 영역을 핵심 관심(core concerns)이라고 부른다. 이 핵심 관심은 기존의 객체지향 분석/설계(OOAD)를 통해 쉽게 모듈화와 추상화가 가능하다. 하지만 횡단 관심은 객체지향의 기본 원칙을 지키면서 이를 분리해서 모듈화하는 것이 매우 어렵다.
이는 횡단 관심의 특징을 모듈로 구현하는 것이 어렵기 때문이 아니다. 문제는 그 구현된 모듈이 다른 핵심 관심을 구현한 모듈과 매우 긴밀하게 결합되어 있기 때문이다. 심지어는 어떤 시스템 영역의 모든 모듈과 연동되어야 하는 경우마저 있다. 그래서 기존의 핵심 관심을 구현한 모듈과 달리 횡단 관심은 그 구현 시점에서 그 분리가 쉽지가 않다. 기존의 OOP 언어와 기술로는 뾰족한 답이 안 나오는 것이다.
대부분의 엔터프라이즈급 애플리케이션은 로그작성(logging)과 보안/인증(security/authentication), 트랜잭션(transaction), 리소스 풀링(resource pooling), 에러 검사(error checking), 정책 적용(policy enforcement), 멀티쓰레드 안전관리(multithread safety), 데이터 퍼시스턴스(data persistence) 등의 적용이 필요하다. 이러한 관심들은 애플리케이션의 핵심 관심과 다른 형태로 존재한다.
예를 들어 은행 업무를 처리하는 시스템을 생각해보면 핵심 관심은 예금입출금, 계좌간이체, 이자계산, 대출처리 등으로 구분할 수 있다. 이는 전체 애플리케이션의 핵심요구사항과 기능들을 구분해서 모듈화할 수 있고 OOP에서라면 클래스와 컴포넌트 형태로 구성이 가능하다. 각각 모듈화된 클래스들은 추상화 등의 작업을 통해 서로 느슨한 연결 상태(loosely coupled)로 만들어서 일부 모듈을 타 시스템에서 재활용할 수 있고, 또 구현의 확장이나 교환도 쉽게 가능하다. 객체지향 기술이 우리에게 준 비전이 바로 이러한 코드의 모듈화와 추상화를 통해 간결하고 깔끔한 코드 작성이 가능하고 재활용성을 뛰어나게 해주는 것이다.
하지만 현실은 그렇지 못하다. 실제로 개발되어 돌아가는 은행 업무의 각 모듈에는 사실 해당 업무를 처리하기 위한 내용만 존재해서는 불완전할 수밖에 없다. 일단 각 업무를 처리하는 클래스와 구현된 메쏘드에는 향후 시스템을 분석하거나 추적을 위해 로그를 작성하는 기능이 필요하다. 또 인증 받은 사용자가 접근하는지를 체크하고 권한 여부를 따지는 보안 기능이 필요하다. 거기다 내부에서 사용하는 퍼시스턴스 처리를 위해 트랜잭션을 시작하고, 또 필요에 따라서 그것을 커밋 또는 롤백하는 부분도 들어가야 한다. 예외 상황이나 문제가 발생했을 때는 그것을 기록에 남기는 부분도 있어야 하고, 필요하면 관리자에게 이메일을 발송해야 한다. 어떤 경우는 특정 업무를 사용하는 사용자의 패턴을 분석하기 위해 통계처리를 위한 기능도 포함 돼야 한다.
이러한 부가적인 기능들은 각각 구현을 독립적인 클래스로 작성될수 있지만 그렇게 구현된 기능들을 호출하고 사용하는 코드들이 핵심 모듈 안에 필요한 영역에 모두 포함될 수밖에 없다. AOP를 적용하지않은 OOP에 의해서만 작성된 간단한 계좌이체 처리를 위한 클래스의 구성을 살펴보자.
로깅, 인증, 권한체크, DB 연동, 트랜잭션, 락킹, 에러처리 등의 기능을 아무리 뛰어난 OOP 기술을 이용해 모듈로 구성하고 추상화를 통해 최대한 독립시킨다고 해도 <리스트 1>에서 보듯이 핵심 모듈의 모든 클래스와 메쏘드 속에 이와 연동되는 부분이 매우 깊이 그리고 상당한 양을 갖으면서 자리 잡고 있다. 실전에서 사용되는 핵심 모듈 클래스들은 이보다 더 복잡하면 복잡했지 더 단순하지는 않을 것이다. 이 코드에서 핵심기능을 담당하고 이 클래스가 설계된 바탕의 핵심 관심을 구현하는 코드는 단 다섯줄에 불과하다. 하지만 그 외의 부가적인 코드를 포함하지 않으면 엔터프라이즈급 애플리케이션으로는 쓸모없을 수밖에 없다.
이러한 횡단 관심을 구현하고 또 그 모듈과 연동이 되는 코드들이 거의 모든 핵심 모듈에 다양한 형태로 존재할 수밖에 없다. 실제로 모듈화가 잘 된 애플리케이션 클래스를 보더라도 핵심기능을 위한 코드보다 이런 저런 부가적인 기능과 처리를 위한 부분의 양이 더 많아지그런데 만약 이렇게 개발된 클래스가 한 수천 개가 되고 대부분이 앞의 코드와 갈이 작성됐다고 했을 때 어느 날 새로운 은행 캠페인이 시작되어서 각 메쏘드에서 사용되는 계좌정보에서 내용을 뽑아내어 통계처리를 하도록 새로운 요구사항이 생겼다면 어떻게 해야 하겠는가?
그와 관련된 모든 클래스들을 찾아 <리스트 1>과 같은 코드 틀 안에 또 새로운 통계처리용 코드를 삽입하는 수밖에 없다. 또 만약 다른 종류의 로깅플랫폼을 사용해 로그처리하는 클래스와 메쏘드가 달라지고 로그 메시지가 변경이 되었다면? 또 개발자들은 모든 클래스 안에 있는 로그관련 코드를 일일이 다 수정해 주는 수밖에 없다. 그러다가 만약 중요한 클래스에서 한두 군데 로그기록 코드가 빠졌고 이로 인해 결과를 확인하는데 문제가 생겼다면? 이를 다시 확인하고 찾아내는 일만 해도 엄청난 작업이 아닐 수 없을 것이다.는 것이 현실이다.
더 나아가서 이렇게 OOAD와 OOP를 이용해 개발한 시스템의 일부 모듈을 타 은행에서 재활용하고 싶다고 생각해보자. 과연 이것이 가능한가? 아마 매우 어려울 것이다. 객체지향 기술이 코드의 재활용성을 높여줄 수 있다는 것은 이론적으로는 맞는 일이지만 현실적으로는 복잡하게 섞여있는 횡단 관심 코드로 인해 이 부분을 다 수정하기전에는 다른 환경에서 같은 핵심기능을 처리하는 코드를 사용한다는 것은 불가능하다. AOP가 적용되지 않은 방식의 프로그램 코드에서 보면 핵심 관심 모듈에서 횡단 관심 모듈을 찾고 사용하는 구조로 작성되어있다. 이렇게 작성된 애플리케이션은 몇 가지 심각한 문제를 가지고 있다.
◆ 중복되는 코드 : 복사&붙이기에 의해 만들어진 여러 모듈에서 중복되는 코드의 문제점은 이미 잘 알려져 있다. 하지만 AOP를 사용하지 않은 대부분의 애플리케이션에서는 어떠한 추상화와 리팩토링을 통해서도 반복되는 코드를 피하기가 어렵다.
◆ 지저분한 코드 : 횡단 관심과 관련된 코드들이 핵심기능 코드 사이사이에 끼어들어가 있기 때문에 코드가 지저분해지고 이에 따라 가독성이 떨어지며 개발자들의 실수나 버그를 유발하고 후에 코드를 유지보수하는데 큰 어려움을 준다.
◆ 생산성의 저하 : 문제영역에 대한 지식과 분석을 토대로 이를 구현해야 하는데 충실해야 하는 애플리케이션 개발자들이 자주 등장하는 횡단 관심을 구현한 코드를 함께 작성해야 하기 때문에 개발의 집중력을 떨어뜨리고 결과적으로 전체 생산성의 저하를 가져온다. 또 모듈별로 개발자들을 구분하고 분산시키는 것이 한계를 가질 수밖에 없다.
◆ 재활용성의 저하 : 이미 언급했듯이 OOP의 장점인 재활용성이 매우 떨어진다.
◆ 변화의 어려움 : 한번 작성된 시스템은 새로운 요구사항이 생겼을 경우에 전체적으로 많은 부분에 영향을 미치는 경우 쉽게 새로운 요구사항을 적용하기 힘들게 된다. 또 새로운 관심영역의 등장이나 이의 적용을 매우 어렵게 한다. 좋은 툴의 도움 없이는 리팩토링을 하는 것도 어렵게 된다.
이러한 문제점을 AOP를 적용하면 어떻게 될 것인지 <리스트 1>의 클래스를 만약 AOP를 이용한 시스템에서 작성을 했을 때의 코드로 살펴보자.
public class AccountTransfer extends AbstractAccountModule {
public void transafer(Accouno fromAcc, Account toAcc, int amount) {
if (fromAcc.hasEnoughMoney(amount) == false) {
throw new AccountException(“not enough money”);
}
fromAcc.withdraw(amount);
toAcc.credit(amount);
}
…
}
놀랍게도 AOP를 적용하지 않았을 때의 코드에 비해 매우 심플한 구조로 바뀌었다. 계좌이체 모듈을 객체지향적으로 설계했을 때 그 설계에 나오는 핵심기능 코드만 존재하게 된 것이다. 그럼 이렇게 작성된 코드가 앞에서 작성된 코드와 똑같은 기능과 방식으로 동작할수 있는 것인가? 그렇다. 새로운 캠페인을 위한 통계코드나 로깅모듈이 바뀌는 일이 일어나도 이 핵심 관심 코드는 결코 변하지 않는다. 문제영역 자체가 바뀌지 않는 한 이 코드는 그대로 사용할 수 있다. 만약 같은 요구조건과 기능을 가진 다른 은행이 이 코드를 가져다 쓰고 싶더라도 거의 코드에 손을 대지 않고 재사용이 가능하다. 물론 로그, 보안, 인증, 퍼시스턴스 등의 처리 방법과 환경이 완전히 달라진다고 해도 상관없다.
그렇다면 그 많은 로그 처리와 보안, 트랜잭션 관리, 예외상황 처리등의 코드는 어디로 간 것인가? 그 부분은 독립된 형태의 단일 모듈로 각각 작성이 되고 AOP를 통해서 필요한 시점에서 핵심 코드에 삽입되어져 동작하게 된다.
AOP의 동작원리
AOP가 핵심 관심 모듈의 코드를 직접 건드리지 않고 필요한 기능이 작동하도록 하는 데는 위빙(weaving) 또는 크로스컷팅(crosscutting)이라고 불리는 특수한 작업이 필요하다. 핵심 관심 모듈이 자신이 필요한 횡단 관심 모듈을 찾아 사용하는 대신에 AOP에서는 위빙 작업을 통해 핵심 관심 모듈의 사이사이에 필요한 횡단 관심 코드가 동작 하도록 엮어지게 만든다. 이를 통해 AOP는 기존의 OOP로 작성된 코드들을 수정하지 않고도 필요한 횡단 관심 기능을 효과적으로 적용해 낼 수 있다.
이런 작업은 기존의 자바 언어와 컴파일러에서는 쉽게 구현할 수 있는 방법이 아니다. 현재의 AOP 언어와 툴이 개발되기 전에는 크게 두 가지 방법으로 AOP와 비슷한 기능을 구현해보려는 시도가 있었다. 첫째는 EJB와 같은 컨테이너 또는 서버를 이용한 방법이다. EJB는 트랜잭션과 보안, 오브젝트 풀링과 같은 횡단 관심 기능을 컨테이너를 통해 그 위에서 동작하는 EJB 모듈에 적용하고 있다. 일종의 엔터프라이즈 서비스 기능인 EJB의 이러한 처리 방식은 EJB가 엔터프라이즈급 애플리케이션 개발과 운영에 적극적으로 수용되는 만큼 중요한 이유 중의 하나이다. 하지만 EJB는 제공할 수 있는 서비스가 매우 제한적이고 임의의 서비스 추가 등이 불가능하기 때문에 본격적인 AOP 방법으로 사용되기는 어렵다. 더군다나 컨테이너 위에서만 특별한 스펙에 따라 작성된 코드와만 연동해서 동작하기 때문에 POJO(Plain Old Java Objects) 기반의 일반 애플리케이션에는 적용하기가 불가능하다.
두 번째 시도된 방법은 JDK 1.3부터 지원되기 시작한 다이내믹 프록시(Dynamic Proxy)를 이용한 인터셉터 체인(interceptor chain)기술이다. JBoss를 비롯한 컨테이너 개발자들을 통해 많이 사용된 방식이다. 하지만 다이내믹 프록시를 이용한 코드는 구현이 매우 복잡하고 특정 구조의 애플리케이션 틀을 따라 작성되어야만 적용 가능하며, 역시 복잡한 프레임워크 내지는 컨테이너의 도움이 필요하다는 면에서 쉽게 일반화되지는 못했다.
본격적인 AOP 기술이 등장한 것은 1990년대 후반 제록스 PARC 연구소에서 그레거 킥제일(Gregor Kiczales)에 의해 AspectJ가 개발되면서라고 볼 수 있다. 그는 Asepct라는 용어와 함께 AOP라는 표현을 처음 사용하기 시작했고, 자바 VM과 호환되는 최초의 AOP 툴인 AspectJ를 구현해 냈다. AspectJ는 자바의 언어를 확장한 형태의 구조로 개발됐는데 자바의 클래스와 유사한 개념인 AOP의 애스팩트(Aspect)를 작성하는 방법을 통해 AOP의 기능을 사용할 수 있도록 했다. 이렇게 만들어진 애스팩트는 AspectJ의 특별한 컴파일러를 통해 자바 VM에서 사용될 수 있는 코드로 만들어진다. 바로 이때 AOP의 위빙 작업이 일어난다. 위빙 작업을 통해 핵심 관심 모듈의 사이사이에 애스팩트 형태로 만들어진 횡단 관심 코드들이 삽입되어 애스팩트가 적용된(woven) 최종 바이너리가 만들어지는 것이다.
AOP를 이용하면 개발자들은 횡단 관심 모듈을 각각 독립된 모듈로 중복 없이 작성하고 이를 위빙을 통해 핵심 관심 모듈과 결합시키기 때문에 서로 독립성을 가진 다 차원의 모듈을 작성할 수 있다. 앞에서 작성한 AccountTransfer 클래스에 적용할 메쏘드 처리의 시작과 끝을 기록하는 로그 작성용 애스팩트의 코드를 살펴보자.
public aspect MethodLoggingAspect {
Logger logger = MyLog.getLogger(“methodcall”);
pointcut methodcall() : execution (void AccountTransfer.transfer(..)) &&
!within(MethodLoggingAspect);
before() : methodcall() {
logger.begin(thisJoinPointStaticPart.getSignature().getName());
}
after() : methodcall() {
logger.end(thisJoinPointStaticPart.getSignature().getName());
}
}
기존의 자바 언어의 문법을 확장한 구조를 가지는 AspectJ에서는 횡단 관심 모듈을 aspect라는 키워드를 가지는 코드로 작성이 가능하다. 앞의 MethodLoggingAspect를 작성한 후 AspectJ의 컴파일러를 사용해 컴파일을 하고 AccountTransfer의 transfer() 메쏘드를 실행시키면 메쏘드의 실행 전후에 메쏘드의 시작과 끝을 알리는 로그가 만들어진다.
이렇게 만들어진 애스팩트는 기존의 코드에 전혀 영향을 주지 않고 자연스럽게 위빙되기 때문에 핵심 관심 모듈을 개발하는 개발자들은 이 부분을 전혀 고려하지 않고 그 모듈 자체의 로직에만 충실히 작성하고 후에 필요한 애스팩트를 만들어 적용할 수 있다. 앞의 애스팩트 코드는 AccountTransfer 클래스의 transfer 메쏘드에만 적용하도록 한 것이다. 필요하면 포인트컷을 적용해서 애플리케이션 내의 모든 메쏘드에 로그를 남기는 애스팩트가 위빙되도록 할 수 있다. 간단히 포인트컷 시그니처만 수정해주면 된다.
pointcut methodcall() : execution(* *.*(..)) && !within(MethodLoggingAspect)
이제 그 숫자와 상관없이 모든 애플리케이션의 메쏘드의 시작과 끝을 로그로 남길 수 있게 되었다. 만약 로그를 작성하는 코드가 달라져야 한다면 이제는 애스팩트만을 수정해주는 것으로 모든 메쏘드의 로깅 방법을 한 번에 변경할 수 있다. AOP를 사용하지 않고 일일이 모든 메쏘드의 로그 코드를 수정했던 것과 비교하면 굉장히 편리해졌음을 알 수 있다.
AOP의 구성요소
AOP에는 새로운 용어가 많이 등장한다. 이 중에서 특히 AOP를 이용해서 개발하는데 필요한 중요한 구성요소들에 대한 정의를 정확히 이해해야 한다.
조인포인트
횡단 관심 모듈의 기능이 삽입되어 동작할 수 있는 실행 가능한 특정위치를 말한다. 예를 들어 메쏘드가 호출되는 부분 또는 리턴되는 시점이 하나의 조인포인트(jointpoint)가 될 수 있다. 또 필드를 액세스하는 부분, 인스턴스가 만들어지는 지점, 예외가 던져지는 시점, 예외 핸들러가 동작하는 위치, 클래스가 초기화되는 곳 등이 대표적인 조인포인트가 될 수 있다. 각각의 조인포인트들은 그 안에 횡단 관심의 기능이 AOP에 의해 자동으로 추가되어져서 동작할 수 있는 후보지가 되는 것이다.
포인트컷
포인트컷(pointcut)은 어떤 클래스의 어느 조인포인트를 사용할 것인지를 결정하는 선택 기능을 말한다. AOP가 항상 모든 모듈의 모든 조인포인트를 사용할 것이 아니기 때문에 필요에 따라 사용해야 할 모듈의 특정 조인포인트를 지정할 필요가 있다. 일종의 조인포인트 선정 룰과 같은 개념이다. AOP에서는 포인트컷을 수행할 수 있는 다양한 접근 방법을 제공한다. AspectJ에서는 와일드카드를 이용한 메쏘드 시그니처를 사용한다.
어드바이스 또는 인터셉터
어드바이스(advice)는 각 조인포인트에 삽입되어져 동작할 수 있는 코드를 말한다. 주로 메쏘드 단위로 구성된 어드바이스는 포인트컷에 의해 결정된 모듈의 조인포인트에서 호출되어 사용된다. 일반적으로 독립적인 클래스 등으로 구현된 횡단 관심 모듈을 조인포인트의 정보를 참조해서 이용하는 방식으로 작성된다. 인터셉터(intercepter)는 인터셉터 체인 방식의 AOP 툴에서 사용하는 용어로 주로 한 개의 invoke 메쏘드를 가지는 어드바이스를 말한다.
위빙 또는 크로스컷팅
포인트컷에 의해서 결정된 조인포인트에 지정된 어드바이스를 삽입하는 과정이 위빙이다. 위빙은 AOP가 기존의 핵심 관심 모듈의 코드에 전혀 영향을 주지 않으면서 필요한 횡단 관심 기능을 추가할 수 있게 해주는 핵심적인 처리과정이다. 다른 말로 크로스컷팅(crosscutting)이라고 하기도 한다. 위빙을 처리하는 방법은 후처리기를 통한 코드생성 기술을 통한 방법부터 특별한 컴파일러 사용하는 것, 이미 생성된 클래스의 정적인 바이트코드의 변환 또는 실행 중 클래스로더를 통한 실시간 바이트코드 변환 그리고 다이내믹 프록시를 통한 방법까지 매우 다양하다.
인트로덕션 또는 인터타입 선언
인트로덕션(Introduction)은 정적인 방식의 AOP 기술이다. 동적인 AOP 방식을 사용하면 코드의 조인포인트에 어드바이스를 적용해서 핵심관심 코드의 동작 방식을 변경할 수 있다. 인트로덕션은 이에 반해서 기존의 클래스와 인터페이스에 필요한 메쏘드나 필드를 추가해서 사용할 수 있게 해주는 방법이다. OOP에서 말하는 오브젝트의 상속이나 확장과는 다른 방식으로 어드바이스 또는 애스팩트를 이용해서 기존 클래스에 없는 인터페이스 등을 다이내믹하게 구현해 줄 수 있다.
애스팩트 또는 어드바이저
애스팩트(aspect)는 포인트컷(어디에서)과 어드바이스(무엇을 할 것인지)를 합쳐놓은 것을 말한다. 필요에 따라서 인트로덕션도 포함할 수 있다. AspectJ와 같은 자바 언어를 확장한 AOP에서는 마치 자바의 클래스처럼 애스팩트를 코드로 작성할 수 있다. AOP 툴의 종류에 따라서 어드바이스와 포인트컷을 각각 일반 자바 클래스로 작성하고 이를 결합한 어드바이저 클래스를 만들어서 사용하는 방법도 있다.
대표적인 AOP 툴
AOP는 OOP의 확장에 가깝기 때문에 전용 언어나 독립된 개발 툴을 가지고 있지 않고 대신 기존의 OOP를 확장한 언어 확장(languageextension) 또는 툴이나 프레임워크 형태로 사용할 수 있게 되어 있다. 현재 자바에서 사용할 수 있게 구현된 AOP의 숫자는 10여개가 된다. 하지만 이 중에서 주로 사용되는 대표적인 AOP 솔루션은 4가지 정도를 꼽을 수 있다. 대표적으로 AOP 구현의 시초가 된 이클립스 프로젝트의 AspectJ를 들 수 있다.
AspectJ는 초기에 제록스 PARC 연구소에서 개발되었다가 2002년에 이클립스 프로젝트에 기증되었고, 현재 IBM의 전폭적인 지원을 받으면서 개발되어 사용되고 있다. 그리고 BEA가 중심이 되어 개발하고 있는 AspectWerkz가 있다. AspectWerkz는 AspectJ와 달리 자바 언어 자체를 확장하지 않고 기존의 자바 언어만으로 AOP의 사용이 가능하도록 되어 있다. 그리고 의존성 삽입(Dependency Injection, 이하 DI) 기반의 프레임워크로 유명한 SpringAOP가 있다. 가장 최근에 등장한 AOP로는 JBossAOP도 있다. SpringAOP와 함께 대표적인 인터셉터체인 방식의 AOP로 꼽힌다.
이렇게 많은 AOP 툴이 존재하는 이유는 아직 AOP가 표준 스펙이나 기준이 없이 현장을 중심으로 빠르게 진화하고 있는 기술이기 때문이다. 기본적인 AOP에 개념은 비슷하지만 실제 구현하는 방법이나 적용 기술은 상당한 차이가 있다. 이 중에서 무엇이 제일 나은 자바 기반의 AOP 하나를 선정하기는 어렵다. 그 이유는 AOP가 사용되는 방식과 목적, 환경에 따라 각기 다른 장단점을 가지고 있기 때문이다. 이렇게 다양한 툴이 존재하는 것이 처음 AOP를 접근하는 개발자들에게는 부담이 되는 것이 사실이다. 하지만 각각의 AOP가 끊임없이 새로운 기능을 시도하고 발전하는 과정을 통해 AOP가 더욱 견고하고 풍부한 기능을 가질 수 있을 것을 기대해 볼 수 있을 것이다.
AspectJ
AspectJ의 가장 큰 특징은 다른 AOP 툴과는 달리 자바 언어를 확장해서 만들어진 구조라는 것이다. 마치 새로운 AOP 언어를 사용하듯이 aspect라는 키워드를 이용해 애스팩트나 포인트컷, 어드바이스를 만들 수 있다. 따라서 일반 자바 컴파일러로는 컴파일이 불가능하고 특별한 AOP 컴파일러를 사용해야 한다. 하지만 이렇게 만들어진 바이너리는 표준 JVM에서 동작 가능한 구조로 되어있기 때문에 특별한 클래스 로더의 지원 없이도 실행 가능하다.
AspectJ는 가장 오래되고 가장 많이 사용되는 AOP 툴이다. 동시에 가장 풍부한 기능을 가지고 있고 확장성이 뛰어나기 때문에 가장 이상적인 AOP 툴로 꼽히고 있다. 하지만 자바 언어를 확장했기 때문에 새로운 문법과 언어를 이해할 필요가 있고 프로젝트 빌드시 특별한 컴파일러를 사용해야 하는 불편함이 있다. 위빙이 컴파일시에 일어나기 때문에 포인트컷에 의해 선택된 모든 클래스들은 애스팩트가 바뀔 때마다 모두 다시 컴파일이 되어야 한다.
AspectJ는 가장 오래되고 가장 많이 사용되는 AOP 툴이다. 동시에 가장 풍부한 기능을 가지고 있고 확장성이 뛰어나기 때문에 가장 이상적인 AOP 툴로 꼽히고 있다. 하지만 자바 언어를 확장했기 때문에 새로운 문법과 언어를 이해할 필요가 있고 프로젝트 빌드시 특별한 컴파일러를 사용해야 하는 불편함이 있다. 위빙이 컴파일시에 일어나기 때문에 포인트컷에 의해 선택된 모든 클래스들은 애스팩트가 바뀔 때마다 모두 다시 컴파일이 되어야 한다.
AspectWerkz
AspectWerkz는 AspectJ와는 달리 자바 언어를 확장하지 않는다. 따라서 표준 자바 클래스를 이용해서 AOP를 구현해 낼 수 있다. 일반 클래스와 메쏘드를 이용해 쉽게 구현이 가능한 어드바이스와 달리 복잡한 문법이 필요한 포인트컷은 별도의 XML 파일을 이용해 설정할 수 있도록 되어 있다. 자바 클래스와 XML 설정 파일의 접근법에 익숙한 개발자들에게는 매우 편리한 접근 방식이라고 볼 수 있다.
최근에는 JDK5의 지원에 따라 어노테이션을 이용할 수 있어 더욱 편리해졌다. 위빙은 특별한 클래스 로더를 이용한 로딩타임 바이트코드 생성을 이용한다. AspectJ 못지않은 다양한 조인포인트와 AOP기능을 지원하고 있으며 편리한 개발을 위한 IDE 플러그인이 개발되어 있다.
JBossAOP
JBossAOP는 앞의 두 가지 툴과 달리 프록시를 이용한 인터셉터 체인을 활용해서 위빙을 처리하는 것이 특징이다. 필요에 따라서 JavaAssist를 통한 바이트코드 조작을 이용하기도 한다. JBossAOP는 원래 JBoss 서버의 EJB를 위한 인터셉터 체인 기술을 통해 발전해 왔다. JBoss는 최초로 디플로이 시점의 코드 생성이 아닌 인터셉터 체인을 이용한 방식으로 EJB 호출과 그 사이에 필요한 엔터프라이즈 서비스 기능의 삽입을 구현해냈고 이를 발전시켜 왔다. 최근 EJB3나 하이버네이트와 같은 POJO 기반의 엔터프라이즈 개발이 활발해지면서 좀 더 범용적으로 AOP를 사용할 수 있는 형태로 JBossAOP를 개발했다.
JBossAOP는 기본적으로 컨테이너에서 동작하지만 컨테이너와 상관없는 독립된 자바 프로그램에서도 사용할 수 있다. 하지만 주 용도는 JBoss 서버와 앞으로 나올 EJB3 컨테이너 등에 AOP를 적용하는 데에 사용되어지는 것이다. AspectWerkz와 마찬가지로 어드바이스는 표준 자바 코드로 작성하고 포인트컷과 다른 설정은 XML 파일이나 JDK5의 어노테이션으로 작성할 수 있다. 아직까지는 JBoss 사용자의 일부에서만 사용되고 있으나 향후 EJB3를 중심으로 한 POJO 기반의 엔터프라이즈 미들웨어 프레임워크가 개발되어짐에 따라 점차로 사용률이 올라갈 것으로 기대된다.
SpringAOP
SpringAOP는 스프링 프레임워크의 핵심기능 중의 한가지로 스프링의 DI 컨네이너에서 동작하는 엔터프라이즈 서비스에서 주로 사용된다. SpringAOP는 다른 AOP와 달리 기존 클래스의 바이트코드를 수정하지 않는다. 대신 JDK의 다이내믹 프록시를 사용해서 프록시방식으로 AOP의 기능을 수행한다. 이 때문에 다른 AOP의 기능과 비교해서 매우 제한적인 부분만을 지원한다.
하지만 SpringAOP의 구현 목적은 엔터프라이즈 애플리케이션에서 주로 사용되는 핵심적인 기능에 AOP의 장점을 살려 이를 스프링내에서 사용하는 것이기 때문에 다른 AOP와 같은 AOP의 복잡한 전체 기능을 굳이 다 필요로 하지 않는다. 프록시 기반의 SpringAOP는 SpringIoC/DI와 매우 긴밀하게 연동이 된다. 따라서 SpringAOP를 사용하는 방법은 스프링 내에 프록시 빈을 설정해서 쉽게 사용할
수 있다.
JDK의 표준 기능만을 사용하기 때문에 특별한 빌드 과정이 필요없고 클래스 로더를 변경한다거나 하는 번거로운 작업이 없다. 대신 조인포인트가 종류가 메쏘드 기반으로 제한되며 같은 클래스 안의 메쏘드 호출이나 콜백 코드를 사용했을 경우에는 프록시를 적용할 수 없는 단점이 있다. 하지만 대부분의 엔터프라이즈 애플리케이션에서 필요로 하는 주요 AOP 기능들을 메쏘드 호출을 기반으로 충분히 처리가 가능하기 때문에 SpringAOP는 그 제한된 AOP 기능에도 불구하고 현장에서 가장 빠른 속도로 적용되어 사용되는 AOP 솔루션 중의 하나이다. SpringAOP는 어드바이스와 포인트컷을 모두 표준 자바 클래스로 작성할 수 있다.
필요에 따라서 포인트컷은 설정 파일 내에서 포인트컷 팩토리 빈을 이용해서 정규식으로 표현이 가능하다. SpringAOP의 최대 단점은 복잡한 프록시 설정 구조이다. Spring빈을 정의한 파일에서 프록시를 정의한 부분의 다른 XML기반의 AOP에 비해서도 복잡한 편인데 이 경우 SpringAOP가 지원하는 AutoProxyingCreatorBean 등을 이용하면 설정 코드를 매우 단순하게 작성하는 것이 가능하다.
AOP의 과제
지난 5년간 AOP가 등장하고 현장에 적용되면서 항상 모든 개발자들에게 좋은 반응만 얻은 것은 아니다. 오히려 OOP에 익숙한 많은 개발자들과 학자, 연구원들에게서 강한 저항을 느낄 수 있을 정도로 반발이 있었다. AOP를 반대하는 대표적인 사람이 자바의 창시자인 제임스 고슬링(James Gosling)인데 그는 AOP가 자바의 OOP 정신과 원칙을 훼손하고 그로인해 실전에서 개발자들에게 도움이 되기보다는 어려움을 주는 문제를 안고 있다고 비판한다.
하나의 새로운 프로그래밍 패러다임이 등장하고 발전하면서 완전히 자리를 잡아가기까지는 그만큼 충분한 시간이 필요할 것이고, 그 이전 세대 패러다임과의 적지 않은 충돌을 예상해야 하는 것은 분명하다. AOP는 다른 프로그래밍 패러다임보다 훨씬 빠른 시간에 현장 실무에 적용되고 자리를 잡아나가고 있고 많은 개발자들의 호응에 힘입어 놀라운 성장을 보여주고 있음에도 주류 기술로 자리 잡기 위해서는 해결해야 할 여러 문제점과 극복할 장벽을 가지고 있는 것이 사실이다.
표준의 부재
AOP는 현재 정해진 표준이 없으며 표준을 정할 기구나 조직도 없다. 용어부터 시작해서 문법, 구현 방법에 이르기까지 AOP와 관련해서 통일된 것이 없다. 따라서 AOP 툴을 만드는 팀과 업체에 따라서 그 내용과 형식이 상당히 차이가 나는 경우도 있다. 비록 기본적인 개념은 공유하고 있다고 하지만 접근 방법이 차이가 나는 것은 AOP가 프로그래밍 표준 기술로 자리 잡는데 많은 장애가 될 수밖에 없다. 이로 인해 처음 AOP를 접하는 개발자들이나 AOP를 공부하려는 사람들에게 혼란을 안겨 주기도 한다.
이런 와중에 AOSD(Internationl Conference on Aspect Oriented Software Development)와 같은 행사들이 꾸준히 열리면서 AOP 연구자들과 툴 개발팀, 개발자들 사이에 많은 의견을 모으고 표준을 위한 노력이 시작되었다는 것은 반가운 소식이다. 또한 최근 AOP툴 업체들 간에 협력이나 통합 구조가 가시적으로 보이고 있다는 것 또한 매우 좋은 현상이라고 본다. 특히 올해 들어 대표적인 AOP 툴인 AspectJ와 AspectWerkz가 협력해서 통합된 AOP 툴을 만들기로 하고 지금 작업이 진행 중이라는 사실은 매우 고무적이다.
게다가 최근 AspectJ 프로젝트를 이끄는 핵심 개발자인 아드리안 콜리어(Adrian Colyer)가 그동안 몸담고 있으면서 AspectJ를 개발해 왔던 IBM을 떠나 SpringAOP를 만든 Interface21에 합류하게 되었다. 이로 인해 3개의 AOP 툴의 장기적인 통합도 기대할 수 있을 것 같다. 하지만 한편으로는 표준을 빨리 제정하기 보다는 아직은 AOP는 발전하고 진화해야 하는 단계에 있기 때문에 다양한 창조적인 시도들이 가능한 현 상태가 어느 정도 더 유지되어져야 할 것이라는 의견도 있다.
학습의 어려움
현장에서 AOP를 접해온 개발자들의 가장 큰 AOP에 대한 비판은 AOP가 너무 어렵다는 것이다. AOP를 배우는 것은 기존 OOP에 대한 완벽한 지식을 바탕으로 한다고 해도 사실 상당한 발상의 전환이 필요하고 그 개념이 난해한 부분이 많다. 새로운 용어들에 익숙해지고 그 의미를 정확히 파악하는 것 또한 만만한 것이 아니다. 게다가 이를 기존 애플리케이션에 어떻게 적용해야할지 그 적용 방법과 아이디어를 내는 것도 수월하지 않다. 더 나아가서 AOP로 개발된 애플리케이션을 보고 이해하는 것도 어려움이 있다고 지적한다.
학습과 적응의 어려움은 OOP가 등장하고 현장에 적용되던 초기와 다를 바 없다고 생각된다. 프로시저 기반의 프로그래밍에 익숙한 개발자들에게 객체지향 언어와 개념은 매우 난해했고 상당한 시간이 지나기까지 개발자들이 매우 어려워했던 것은 사실이다. 아직도 OOP의 개념과 원리에 대해서 또는 OOAD에 대해 힘들어 하는 개발자가 많은 게 사실이다. AOP도 또한 초기에는 그러한 적응의 과정이 필요할 것이다. 더 많은 개발자들과 이론가들이 충분히 그 기술과 개념에 익숙해져야 할 시간이 필요할 것이고, 또 AOP 기반의 애플리케이션을 설계하고 구현하는데 필요한 다양한 방법론과 설계 기술들이 등장하게 될 것이라고 보여 진다.
문제는 후자의 경우인데 AOP로 만든 프로그램은 프로그램의 흐름을 이해하고 추적하기가 매우 힘들다는 주장이다. 이는 AOP에 대한 오해에서 비롯된 주장이라고 보여진다. 필자를 포함해서 많은 AOP를 이용해서 애플리케이션을 개발하는 개발자들의 공통적인 의견은 AOP를 적용한 후가 그렇지 않을 때에 비해 애플리케이션의 구성과 코드가 깔끔해졌고 그로 인해 전체 애플리케이션을 이해하고 교육하고 유지보수하기가 한결 쉬워졌다는 것이다. 물론 AOP는 매우 다차원적으로 시스템을 이해하고 접근하기 때문에 역시 해석 또한 다차원적이어야 한다는 부담감이 있다. 대신에 각 차원의 독립된 관점에 따라서 애플리케이션을 살펴보고 이를 통합해서 생각하는 훈련만 된다면 이전에 많은 다른 차원의 코드들이 섞여있던 때에 비해 애플리케이션에 대한 이해는 훨씬 수월해질 것이라고 기대된다.
OOP와의 충돌
AOP의 비판자들 중 상당수는 AOP가 OOP의 정신을 훼손하고 OOP의 중요 원칙을 깨뜨린다고 한다. 이 부분은 아직도 많은 논쟁이 진행중인 부분이다. 대표적으로 AOP는 OOP의 캡슐화를 깨뜨리고 객체의 내부에 직접적인 영향을 줄 수 있기 때문에 객체지향적인 장점을 손상하고 시스템을 복잡하게 만든다고 한다. 기존의 클래스와 코드의 어떤 부분에라도 영향을 줄 수 있다는 면에서 일견 일리가 있는 주장이다. 하지만 AOP가 없이 그러한 횡단관심 기능을 핵심 모듈 내부에 직접 작성해서 쓰는 것은 과연 객체지향의 원칙에 맞는 일인가? 그게 아니라면 현실적으로 객체지향적인 방법으로 그 부분을 해결할 수 있는 효과적인 방법을 제시해야 할 것이다. 결국 OOP의 한계와 약점이있는 곳에서 AOP가 출발하기 때문에 이것을 노골적으로 드러내는 AOP가 OOP와 충돌하는 것은 어쩔 수 없는 현실이라고 본다.
다만 AOP 만능주의자가 되어 OOP적인 바른 접근과 설계를 뒷전으로 하는 것은 진정한 AOP가 아니라고 생각된다. AOP가 가장 큰 영향을 줄 수 있는 것 중의 하나는 개발방법론을 비롯한 소프트웨어 디자인과 아키텍팅 부분이다. 기존의 BUFD(Big Up Front Design)에 익숙한 객체지향 아키텍트는 딜레마에 빠지기가 쉬운데 이는 초기디자인은 어느 정도까지 하는 것이 적당한 것인지 항상 고민하기 때문이다. 너무 과도한 디자인은 향후 시스템의 개발 과정이나 개발 후에 더욱 발전하거나 변화하는데 장애가 되기 때문에 초기 디자인이 어디까지 나아가야 할 것인가 큰 고민일 수밖에 없다. 이런 상황에서 AOP의 도입은 초기 디자인의 부담을 크게 덜어 줄 수 있다.
핵심 디자인을 거의 손대지 않고도 시스템 전반에 영향을 주는 횡단 관심 모듈을 독립성 있게 개발하고 전 시스템에 걸쳐 자유롭게 삽입, 제거가 가능해지기 때문이다. 따라서 AOP는 XP(Extreme Programming) 방식을 따르는 개발자들에게는 상당히 막강한 무기가 되기도 한다. 동시에 전통적인 디자인 우선 방식을 선택한 아키텍트들에게도 많은 도움이 되고 있는 것도 사실이다. 여기서 한 가지 AOP에 대한 오해가 생기기 쉬운데 AOP가 잘 설계되지 못한 디자인을 완전하게 만들어 줄 것이라는 환상이다. 이는 결코 그렇지 않다. AOP는 이미 잘 설계되어 있는 디자인을 더욱 쉬운 방법으로 적용할수 있게 만들어줄 뿐이다. 이런 면에서 AOP를 쓴다고 해서 기존의 OOP적인 설계와 접근방법을 포기하거나 가볍게 여기는 것은 잘못된 선택이 분명하다.
AOP의 미래
AOP는 갑자기 어디서 떨어진 마법의 만능도구는 아니다. 오히려 현장에서 개발에 매진하는 개발자들이 스스로 기존 기술과 방법의 문제점을 극복하기 위해 찾아낸 개발자를 위한 도구이다. 모든 소프트웨어 기술이 다 그렇듯이 AOP도 결국 좀 더 효과적인 방법으로 더 나은 품질의 제품을 개발하는데 그 존재 가치가 있다. 최근에 불고 있는 POJO 기반의 개발 기술의 열풍은 이러한 근본적인 문제에 대한 개발자 스스로의 반성이 현실로 나타난 것이다. 아드리안 콜리어는 POJO 기반의 애플리케이션 개발의 3대 기술을 AOP, DI, 어노베이션이라고 하고 이 3가지가 앞으로의 시대를 주도하는 핵심 개발 기술이 될 것이라고 이야기한다. 이미 어노테이션은 자바 5의 표준 기술로 자리를 잡았고 의존 삽입 또한 다양한 프레임워크의 지원 하에 매우 빠른 속도로 사실상의 표준 기술로 자리 잡고 있다. 이제 AOP가 주류 기술로 자리 잡을 때가 다가왔다고 보여진다. AOP를 통해 더 나은 애플리케이션 개발을 경험하는 개발자들이 국내에도 많아지게 되기를 기대해본다.
* 이 기사는 ZDNet Korea의 제휴매체인 마이크로소프트웨어에 게재된 내용입니다.