-
7. AOP (Aspect Oriented Programming: 관점 지향 프로그래밍)Back-end Developer/Spring Framework, 설정 및 실습 2018. 12. 19. 16:16
드디어 AOP가 등장하네요.
관점지향 프로그래밍... 말로만 보면 관점을 중심으로(?) 프로그래밍을 한다.. 라는 뜻인데 뭔가 애매하죠?
약간 객체지향을 처음 만났을때와 비슷한 느낌..하하..
아직 그것도 잘 모르는데 ^ㅠ^일단, 개념과 쓰이는 용어등을 조금 찾아봤어요.
AOP? (Aspect Oriented Programming)
관점지향 프로그래밍, Cross Cutting concern(횡단 관심사)의 분리를 허용하여 Modularity 증가를 목적으로하는 방법.
그 자체로 프로그램을 하나 형성하는 것이 아닌 객체제향 코드에서 이루어지며, 이를 보조해주는 역할을 함.
'Modularity를 증가시킨다'라는 말은 우리가 잘 아는 코드의 중복을 줄이고 재사용성을 높인다는 의미입니다.
Cross Cutting concern은 객체지향과 위의 Modularity와 관련이 있습니다.
횡단 관심사란 다른 관심사에 영향을 미치는 프로그램의 Aspect입니다.
개발되는 프로그램이 매우 커지면서 모듈을 통해 코드를 간소화 시켜도, 그 내부에서도 중복되는 코드가 생기기 마련인데
이러한 것들을 '횡단 관심사'라고 하며, 이들을 Aspect를 통해 캡슐화를 하여 Modularity의 증가를 목표로하는 것이 바로 AOP입니다.
용어 정리
- Aspect (= Advisor)
여러 객체에 공통으로 적용되는 공통 관심사항Transaction, 보안Advisor = Advice + Point cut- Advice
'언제 공통 관심 기능을 핵심 로직에 적용할지'를 정의Aspect를 처리하는 기능 그 자체Point cut을 통해 어디서 Weaving하는지 정의- Joint Point
Advice가 적용 될 수 있는 지점 (특정 작업이 시작되는 시점)Class의 instance 생성 시점, 메소드 호출 시점, 예외 발생 시점- Point cut
실제로 Advice가 적용되는 Joint point를 나타냄여러개의 Joint point를 하나로 결합한 것을 Point cut이라고함- Weaving
Advice(공통 코드)를 핵심 로직에 적용하는 것Aspect를 Target에 제공해 새로운 Proxy를 생성하는 과정- Target
Advice를 받는(핵심 로직을 구현하는) Class- Proxy
Target에 Advice가 적용된 후 생성된 객체용어는 간단하게 이해할 수 있을 정도로만 써놨습니다.
실습간에 계속 위의 용어들이 등장 할 텐데, 어느정도 이해해보시고 찾아보신 후에 넘어가주세요~
실습
우선 프로젝트를 하나 새로 생성하기엔 번거롭고, 지난번에 했던 실습을 이용해서 해도되니까, DI 실습 프로젝트를 복사해주세요.
실습 준비
프로젝트 명은 testAOP로 지정했습니다.
파일내에 package 경로가 죄다 DI쪽 경로로 잡혀있을거에요. 당연하죠?
그러니 당연하게 고쳐줍시다.
MainApp class
12345678910111213141516package kr.ac.snut.spring.aop;import org.springframework.context.support.ClassPathXmlApplicationContext;public class MainApp {public static void main(String[] args) {ClassPathXmlApplicationContext context =new ClassPathXmlApplicationContext("/kr/ac/snut/spring/aop/beans/animal.xml");PetOwner person = (PetOwner) context.getBean("petOwner");person.play();context.close();}}cs pom.xml artifact id
pom.xml은 코드를 찾아서 하나씩 고치기 귀찮으니 위와같이 쓰시고 변경하세요.
실행해서 작동 잘 되는지 확인한번 해보고 넘어갑시다!
저는 별다른 에러가 발생하지 않았지만, 혹시 발생한 분들은 에러코드 잘 보고 수정해보세요.
다음으로 pom.xml -> dependencies -> spring-aspect 추가해주세요.
저장해서 꼭 다운로드 완료 시켜주세요.
검색해봤는데 저는 이번에도 뜨지 않아서 그냥 직접 쳤습니다..ㅠㅠ (
망할 깡통...빨리 바꾸고 싶네요 ㅎㅅㅎ)Logger Class 생성
Log를 발생시킬 Log Class를 하나 만들어 볼게요.
그리고 해당 Class 내부엔 이전에 생성했던 동물의 sound 메소드와 관련될 메소드를 하나 정의하겠습니다.
이름은 sound 메소드와 관련되어있으니 aboutToSound라고 정의할게요. (뭔가 가독성이 팍! 올라가는 느낌이죠? ^____^)
이런식으로 정의해서 sound 메소드가 발생전에 이 aboutToSound를 발생시킵니다!!
1234567891011package kr.ac.snut.spring.aop;public class Logger {private void selectSound() {}public void aboutToSound() {System.out.println("before: about to sound");}}cs 이렇게하시면 이제 Logger Class는 Aspect가 되고,
aboutToSound 메소드는 sound 메소드 이전에 발생되므로 before Advice이구요,
sound 메소드는 Joint point가 됩니다.
위에 써둔 정의에 비교해서 이해해보세요! 아직은 근데 완전한 기능을 구현한게 아니니까요.
이해 안되어도 괜찮아요. 이제 이 Class를 Bean에 등록해볼게요.
Bean 등록 및 configuration 설정
animal.xml에서 이전에 했던 것 처럼 Bean 등록 해주세요.
이제 AOP configuration을 잡아줘야합니다.
Namespace에서 aop를 체크해서 탭 생성 하고, xml 코드가 정상적으로 붙었는지 확인 해주세요.
12345678910111213141516171819202122232425<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd"><context:annotation-config></context:annotation-config><bean id="dog" class="kr.ac.snut.spring.aop.Dog"><property name = "dogName" value = "poodle"></property><qualifier value="qf_dog"></qualifier></bean><bean id="cat" class="kr.ac.snut.spring.aop.Cat"><property name = "catName" value = "siam"></property><qualifier value="qf_cat"></qualifier></bean><bean id = "petOwner" class = "kr.ac.snut.spring.aop.PetOwner"></bean></beans>cs configuration도 설정할게요.
aop 탭 -> beans에서 Insert config element 선택
Aspect 생성 및 설정
이 config 안에 Aspect를 생성하고, 여기에 포함 되어야 할 Advice와 Point cut을 생성할게요.
우선 aop 탭에서 생성한 config에서 Aspect부터 생성해주세요.
'logger라는 이름을 가지는 Class, 즉 해당 bean을 Aspect로 사용함'으로 설정하고 Id는 임의로 정해주세요.
(reference로 logger를 사용한다.)
생성된 Aspect에 다시 Point cut도 만들어주세요.
Point cut도 Id 임의로 써주시고, 아래와 같이 정의해주세요.
Asterisk(*)는 모두를 포함하는 의미가 있습니다. Cat, Dog 두개의 Class 모두에 적용 시킬것이니, 당연히 이게 맞겠죠?
따로따로 해버린다면, 아까 우리가 공부했던 Modularity가 매우 떨어지겠죠...ㅠ_ㅠ
마지막으로 Advice 생성할게요. 아까 말씀드렸지만, 우리의 Advice는 sound 메소드 실행 전!
즉, before Advice라고 했으니 거기에 맞춰 만들게요.
Aspect에서 before Advice 추가해주세요.
Point cut reference는 당연히 아까 Point cut 생성시 설정했던 'Id: selectSound' 넣어주고,
메소드는 Logger에서 정의했던 aboutToSound 메소드 넣어주세요.
Aspect가 완성이되었네요.
12345678910111213141516171819202122232425262728293031323334353637383940<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd"><context:annotation-config></context:annotation-config><bean id="dog" class="kr.ac.snut.spring.aop.Dog"><property name = "dogName" value = "poodle"></property><qualifier value="qf_dog"></qualifier></bean><bean id="cat" class="kr.ac.snut.spring.aop.Cat"><property name = "catName" value = "siam"></property><qualifier value="qf_cat"></qualifier></bean><bean id = "petOwner" class = "kr.ac.snut.spring.aop.PetOwner"></bean><bean id="logger" class="kr.ac.snut.spring.aop.Logger"></bean><aop:config><aop:aspect id="myAspect" ref="logger"><aop:pointcutexpression="execution(void kr.ac.snut.spring.aop.*.sound())"id="selectSound" /><aop:before method="aboutToSound"pointcut-ref="selectSound" /></aop:aspect></aop:config></beans>cs 실행 결과
잘되는지 확인 합시다.
AspectJ를 통해 Annotation 사용하기
이전에 DI 처리를 annotation을 통해 대체했었는데, 자바코드에서 이루어졌었죠.
Aspect도 AspectJ annotation을 사용해볼게요. 얘도 당연히 자바 코드에서 이루어집니다.
물론 xml에 AspectJ 사용할게요! 라고 선언정돈 해줘야겠죠.
animal.xml -> aop 탭 -> bean 추가 -> beans -> Insert aspectj autoporxy element 선택
animal.xml code
1234567891011121314151617181920212223242526272829<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd"><context:annotation-config></context:annotation-config><bean id="dog" class="kr.ac.snut.spring.aop.Dog"><property name = "dogName" value = "poodle"></property><qualifier value="qf_dog"></qualifier></bean><bean id="cat" class="kr.ac.snut.spring.aop.Cat"><property name = "catName" value = "siam"></property><qualifier value="qf_cat"></qualifier></bean><bean id = "petOwner" class = "kr.ac.snut.spring.aop.PetOwner"></bean><bean id="logger" class="kr.ac.snut.spring.aop.Logger"></bean><aop:aspectj-autoproxy></aop:aspectj-autoproxy></beans>cs Logger Class 코드도 변경해볼게요.
Aspect annotation 추가하고, Point cut에 쓰여있던 코드를 자바코드로 옮겨주세요.
Joint point도 입력하고, Id는 메소드로 설정합시다.
마지막으로 before Advice 메소드와 Point cut 처리를 위해 위에 선언해둔 메소드로 표시해주세요.
1234567891011121314151617181920package kr.ac.snut.spring.aop;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.aspectj.lang.annotation.Pointcut;@Aspectpublic class Logger {@Pointcut("execution(void kr.ac.snut.spring.aop.*.sound())") // expressionprivate void selectSound() { // signature(id)}@Before("selectSound()")public void aboutToSound() {System.out.println("before: about to sound");}}cs 코드가 이렇게 완성되면, 이제 기능이 어떻게 돌아갈지 어느정도 감이 잡히실 겁니다.
'Point cut 실행시, sound 메소드를 불러올것인데, 그전에 아래의 before Advice를 실행할게요.'라고 쓰여있죠.
이제 PetOwner로 가서 value를 'qf_dog', 'qf_cat'으로 바꿔가며 실행해보세요.
작동 잘 됩니다. 하하하핳
오늘 했던 작동들은 간단히 얘기해보면,
PetOwner나 Cat, Dog와 같은 application에 영향을 주지 않고, Log에 관련된 작동을 별도의 모듈로 처리해 주었습니다.
위에서 말씀드린바와 같이 이렇게 별도의 Aspect로 빼냄으로써 코드가 깔끔해지고 재 사용성이 증가된 것이죠.
뭔가 객체지향을 돕는다는 느낌이 와 닿으시나요?
저는 와 닿는데.. 하하...; 기본기가 차곡차곡 쌓이는 느낌이 드네요. 고생하셨어요!!
반응형'Back-end Developer > Spring Framework, 설정 및 실습' 카테고리의 다른 글
9. JDBC 설정 (0) 2018.12.28 8. MySQL workbench 활용. (0) 2018.12.21 6. Annotation으로 의존성 주입하기 (0) 2018.12.16 5. DI (Dependency Injection: 의존성 주입) (0) 2018.12.13 4. JSTL를 이용한 View에서 기능 처리 (참고) (0) 2018.12.11