여행을 개발하다

관점 지향 프로그래밍(Aspect-oriented programming, AOP) - xml 파일 버전 본문

Spring/Spring

관점 지향 프로그래밍(Aspect-oriented programming, AOP) - xml 파일 버전

yhtragramming 2019. 5. 16. 10:58

안녕하세요!!

 

오늘 포스팅 할 내용은

'관점 지향 프로그래밍(Aspect-oriented programming, AOP)'입니다.

그 중에서도 xml 파일을 이용하는 방식으로 해볼껀데요.

 

지금까지는 고유한 기능을 가지고 있는 클래스, 인터페이스의 객체들을 조합하여 프로그램을 만드는

'객체 지향 프로그래밍(Object Oriented Programming, OOP)'이 주였지만,

트랜잭션이나 로그처럼 반복되는 중복 코드가 여전히 발생한다는 한계점이 있었습니다.

 

이러한 한계점을 보완하기 위해 고안된 방식이 '관점 지향 프로그래밍, AOP' 입니다.


1. AOP, Aspect-oriented programming?

- 객체 지향 프로그래밍에서 여전히 중복 코드(횡단 관심사)가 발생한다는 단점을 해결하기 위해 고안된 방식으로, 개발코드에서는 비즈니스 로직에 집중하고 실행시에는 비즈니스 로직 앞, 뒤 등 원하는 지점에 해당 공통 관심사를 수행하게 하여 중복 코드를 줄일 수 있는 프로그래밍 방식.

 

- 모듈의 핵심 기능 이외의 기능이 하나의 모듈에 응집되지 않도록 하기 위한 기술

 

2. AOP 관련 용어

1. aspect : 공통 기능.

2. advice : aspect 자체를 의미.

3. joinpoint : advice를 적용해야 하는 필드나 메소드. (스프링은 메소드에 적용하는 기능만 지원)

4. pointcut : advice가 실제로 적용되는 부분.

 

3. AOP의 종류

1. before : 핵심 기능이 실행되기 전에 advice를 실행한다.

2. after-returning : 핵심 기능이 정상적으로 종료된 후 advice를 실행한다.

3. after-throwing : 핵심 기능을 실행하는 도중에 exception이 발생되면 advice를 실행한다.

4. after : 핵심 기능을 실행하는 도중에 exception이 발생 여부에 관계없이 advice를 실행한다.

5. around : 핵심 기능이 실행되기 전과 실행되고 난 후 exception 발생 여부에 관계없이 advice를 실행한다.

 

4. AOP를 위한 사전 준비

관점 지향 프로그래밍을 위해서는 몇 가지 사전 준비 작업이 필요합니다.

 

먼저, 지금까지 모든 스프링 프로젝트에 녹아있었지만, 자세히 보지 않았던 하나의 파일이 있는데요.

바로 'Maven Dependencies' 폴더에 있는 'pom.xml'이라는 파일입니다.

이 파일에 코딩을 조금 해주겠습니다.

코드를 추가하기 위해 다음 사이트로 접속 후, AspectJ Weaver의 1.8.0 버전으로 Maven 코드를 복사합니다. (이는 컴퓨터에 따라 버전이 상이할 수 있습니다)

 

mvnrepository.com

 

위의 주석까지 모두 복사하실 필요는 없습니다!

 

이제는 Spring의 pom.xml 파일을 여시고, 다음 위치에 해당 코드를 붙여넣습니다.

 

이렇게요!

 

그리고 'alt + F5'를 눌러서 해당 프로젝트를 한 번 업데이트 해주시면 됩니다!^^

 

코드가 추가되면 'c 드라이브 << 사용자 << .m2 << repository << org << aspectj << aspectjweaver << 1.8.0'폴더에 아래와 같이 파일이 생성되는데요. 그 중 'aspectjweaver-1.8.0.jar' 파일의 크기가 0KB로 표시되면 정상적으로 설치가 되지 않은 것이므로, 다시 설치하시기 바랍니다.

 

마지막으로 xml 파일의 Namespaces에서 'aop'를 추가해주시면 됩니다.

 

이와 동시 xml 파일 상단에는 aop bean이 import됩니다.

 

5. 공통 기능과 핵심 기능의 정의

관점 지향 프로그래밍의 최종 목적은 결국 중복 코드를 최소화하는 것입니다.

즉, 여러 클래스와 인터페이스들의 공통 기능은 따로 뽑아 관리하여 효율을 높이는 것이죠.

 

그러기 위해서는 클래스와 인터페이스 내, 공통 기능이 무엇이고 핵심 기능이 무엇인지를 정하는 작업이 선행되어야 합니다.

 

지난 포스팅에서 활용했던 student 클래스를 예제로, 학생의 이름, 나이, 학년, 반 번호를 출력하는

'getStudentInfo()' 메소드를 하나 만들겠습니다. 그리고 이를 student 클래스의 핵심 기능이라고 상정하겠습니다.

 

그 다음에는 'Worker'라는 클래스를 하나 더 만들겠습니다.

 

그리고 Worker 클래스의 핵심 기능'getWorkerInfo()'라는 메소드라고 하겠습니다.

 

여기까지 핵심 기능을 정했다면, 이제는 두 Class가 공통으로 실행할 기능을 하는 또 다른 Class를 하나더 만들겠습니다.

 

Class의 이름은 'AopConfig'로 하고, AOP의 4가지 종류를 간단히 출력하는 메소드들로 구성하겠습니다.

 

6. AOP 설정

이제는 xml 파일에서 AOP를 설정하는 작업을 진행하겠습니다.

기본적으로 각 Class의 Bean과 공통 기능을 정의한 AOP Class의 Bean도 하나씩 만들어주겠습니다.

 

 

6-1. <aop:config> - AOP 설정 범위 태그

이제 본격적인 AOP 설정을 할껀데요, AOP 설정은 <aop:config> </aop:config>태그로 정의합니다. 당연히 태그 범위내에서 설정한 것만이 유효하고요.

 

6-2. <aop:aspect> - AOP 공통 기능 정의 태그

그 다음으로, <aop:aspect> 태그로 공통 기능을 정의해주도록 하겠습니다.

 

<aop:aspect> 태그는 크게 id 속성과 ref 속성으로 이루어집니다. id 속성은 AOP 메소드가 정의된 클래스를 구분만 하면 되므로 마음대로 적어도 관계 없지만, ref 속성반드시 실행할 AOP 메소드가 정의된 bean의 id 속성에서 정의한 내용을 적어줘야 합니다. 그래서 id는 임의로 'logger' 정하고, ref는 공통 기능을 정의한 AOP Class의 Bean id인 'logger'로 적어줘야 합니다.

 

 

 

6-3. <aop:pointcut> - advice가 실제로 적용될 부분을 정의하는 태그

<aop:pointcut> 태그는 advice가 실제로 적용될 부분을 정의하는 태그입니다.

속성에는 'id''expression'이 있습니다.

 

id 속성값은 <aop:aspect> 속성의 id 속성처럼 사용자가 임의로 정할 수 있습니다.

반면, expression 속성값within() 혹은 execution()으로 나누어지는데요.

 

속성 값을 within()으로 지정할 경우 pointcut의 적용 범위는 클래스 단위로 지정되며,

execution()로 지정할 경우 pointcut의 적용 범위는 메소드 단위가 됩니다.

 

그렇다면 어떤 클래스와 메소드에 적용할지도 알려줘야 하겠네요.

 

그런데 가끔은 한 개 이상의 클래스와 메소드를 expression 속성 값으로 넣어줘야 될 경우가 생기는데요. 클래스와 메소드가 동일한 철자를 포함하고 있다면, 와일드 카드 문자도 사용 가능합니다.

 

다음은 expression 속성 값을 와일드 카드 문자를 사용해서 지정하는 방식과 그 의미입니다.

 

-----------------------------------------------------------------------------------------------------------------------

※ within(*)

현재 프로젝트의 모든 패키지에 있는 모든 클래스의 메소드에 AOP를 적용시킨다.

 

※ within(springAOP_xml_0726.*)

springAOP_xml_0912 패키지의 모든 클래스의 메소드에 AOP를 적용시킨다.

 

※ within(springAOP_xml_0912.W*)

springAOP_xml_0912 패키지의 W로 시작하는 모든 클래스의 메소드에 AOP를 적용시킨다.

 

W* : W로 시작하는

*t : t로 끝나는

*e* : e를 포함하는

 

※ execution(* springAOP_xml_0912.*.*())

springAOP_xml_0912 패키지의 모든 클래스의 모든 메소드에 AOP를 적용시킨다.

 

※ execution(* springAOP_xml_0912.S*.*())

springAOP_xml_0912 패키지의 S로 시작하는 클래스의 모든 메소드에 AOP를 적용시킨다.

이 때, "*"와 패키지 명 사이에는 반드시 공백이 있어야 한다.

 

※ execution(* kr.koreait.springAOP_xml_0912.S*.b*())

springAOP_xml_0912 패키지의 S로 시작하는 클래스의 b로 시작하는 메소드에 AOP를 적용시킨다.

----------------------------------------------------------------------------------------------------------------------

 

그럼 간단하게 before부터 시작하겠습니다.

 

pointcut은 새로 만든 프로젝트에 있는 모든 클래스로 정할 것이고, 이를 식별하는 id는 'beforeMethod'로 합니다. 참고로 id는 어떤 것으로 해도 상관 없습니다.

 

aop의 종류는 before이므로, <aop:before> 태그를 만들어줍니다. <aop:before> 태그의 속성 값 method와 pointcut-ref가 있는데, method에는 <aop:aspect> 태그의 ref 속성이 참조하는 bean에서 실행할 메소드 이름을 적어줍니다.

 

또한, pointcut-ref 속성에는 <aop:pointcut> 태그의 id 속성에서 정의한 내용을 적어줘야 합니다.

 

그리고 항상 그래왔듯이, 메인 클래스를 하나 만들어서 실행해보겠습니다.

 

실행 결과를 잠시 예측해보면, pointcut이 전체 클래스이기 때문에 main 함수 내에서 실행되는 모든 메소드에 before가 적용되어야 하며, 이는 메소드 시작 전에 실행되어야 합니다.

 

 

실행 결과, 잘 나오네요!^^

 

동일한 방법으로 aop 종류만 바꾸어 적용해볼 수 있습니다.

 

* after-returning 적용

 

* after-throwing, after 적용

after-throwing, after는 메소드를 조금 바꾸어서 진행해보겠습니다. 왜냐하면, 둘 다 예외 처리와 관련이 있기 때문입니다.

 

다만, after-throwing은 핵심 기능을 실행하는 도중에 exception이 발생되면 advice를 실행한다는 것, after는 exception 발생에 상관없이 advice를 실행한다는 차이가 있었습니다.

 

그래서 student 클래스의 getStudentInfo() 메소드에 age를 0으로 나누는 작업을 추가하여, 어이가 없는 예외를 하나 만들도록 하겠습니다.

 

그리고 afterThrowing 메소드만 설정하여 실행하였습니다.

 

그 결과 afterThrowing 메소드는 잘 실행이 되는데, 핵심 기능이 정상적으로 종료된 후 advice를 실행하는 after-returning은 끝내 실행되지 않았습니다.

 

after를 추가하고 실행해도 동일한 결과를 얻습니다.

 

 

나머지는 원리를 참고하시면서 테스트 해보시기 바랍니다.

* around

그런데 우리가 하나 빼먹은 것이 있죠?

 

바로 around입니다. around는 핵심 기능이 실행되기 전과 실행되고 난 후, exception 발생 여부에 관계없이 advice를 실행하는 것이었는데요. 여기서 한 가지 조심해야 할 것은, around는 실행 전과 실행 후 핵심 기능의 형태가 어떻게 바뀔지 모른다는 것입니다.

 

그래서 형이 바뀌어 출력된다는 예외를 항상 생각해야 하며, 이에 따라 리턴 자료형도 object가 되어야 합니다. 부가적으로, 핵심 기능은 ProceedingJoinPoint라는 인터페이스의 객체로 넘겨받아 처리합니다.

 

마지막으로, 예외는 try ~ catch가 아닌 throws와 try ~ finally로 처리합니다.

 

메소드의 내용은 try 구문 전의 경우 joinPoint의 객체가 넘어오기 전에 실행할 내용을, try 구문 안에는 jointPoint의 객체로 넘어온 기능을 실행한 결과를, finally 구문 안에는 핵심 기능이 끝난 후에 실행할 것을 적어주면 됩니다.

 

around 설정도 하나 만들어줍니다.

(기능이 제대로 실행되는지 보기 위해, 기존에 student, worker 클래스에서 만들었던 'int result = age / 0;'은 지워주도록 하겠습니다!)

 

 

마지막으로 before와 around의 실행 순서는 어떻게 정해지는지 문득 궁금해졌는데요.

단순히 xml 파일에서의 코딩 순서에 따라 정해진다고 보시면 됩니다.

around를 before 앞에서 정의해주면 순서가 바뀝니다.

 


지금까지 관점 지향 프로그래밍(Aspect-oriented programming, AOP)의 개요, AOP를 xml 파일로 구현하는 방법 및 AOP의 종류와 특징에 대해 알아보았습니다.

 

다음 시간에는 java 파일로 AOP를 구현해보도록 하겠습니다.

 

감사합니다!! : )

Comments