AOP (Aspect Oriented Programming)
관점 지향 프로그래밍
: 프로그램이 실행되는 처리 과정을 바라보는 시점에 따라 분할하여 인식하고,
해당 시점에 추가적 구현을 하는 것.
1. AOP 용어 정리
Target
- 핵심 기능을 담고 있는 모듈로, 타겟은 부가기능을 부여할 대상이 된다.
Advice
- 관점 시점
- 타겟에 제공할 부가기능을 담고 있는 모듈.
Joinpoint
- 어드바이스가 적용될 수 있는 위치.
- 타겟 객체가 구현한 인터페이스의 모든 메소드는 조인 포인트가 된다.
- 진행중인 흐름. 현재 상태.
Pointcut
- 어드바이스를 적용할 타겟의 메소드를 선별하는 정규표현식
- 누구를 쪼개볼 건지.
- 포인트컷 표현식은 execution으로 시작하고 메소드의 Signature를 비교하는 방법을 주로 이용한다.
Aspect
- AOP의 기본 모듈.
- Aspect = Advice + Pointcut
- 싱글톤 형태의 객체로 존재한다.
Advisor
- Advisor = Advice + Pointcut
- 어드바이저는 Spring AOP에서만 사용되는 특별한 용어이다.
Weaving
- 포인트컷에 의해 결정된 타겟의 조인포인트에 부가기능(어드바이스)를 삽입하는 과정
- AOP가 핵심기능(타겟)의 코드에 영향을 주지 않으면서 필요한 부가기능(어드바이스)를 추가할 수 있도록 해주는 핵심적인 처리과정.
2. Spring AOP의 구현 방식
1. XML 기반의 POJO 클래스를 이용한 AOP 구현
- 부가기능을 제공하는 Advice 클래스를 작성한다.
- XML 설정 파일에 <aop:config>를 이용해서 애스펙트를 설정한다.
(즉, 어드바이스와 포인트컷을 설정함)
2. @Aspect 어노테이션을 이용한 AOP 구현
- @Aspect 어노테이션을 이용해서 부가기능을 제공하는 Aspect 클래스를 작성한다.
- 이 때 Aspect 클래스는 어드바이스를 구현하는 메서드와 포인트컷을 포함한다.
- XML 설정 파일에 <aop:aspectj-autoproxy />를 설정한다.
3. Advice의 종류
Around 어드바이스
: 타겟의 메서드가 호출되기 이전(before) 시점과 이후 (after) 시점에 모두 처리해야 할 필요가 잇는
부가기능을 정의한다.
-> Joinpoint 앞과 뒤에서 실행되는 Advice
Before 어드바이스
: 타겟의 메서드가 실행되기 이전(before) 시점에 처리해야 할 필요가 있는 부가기능을 정의한다.
-> Jointpoint 앞에서 실행되는 Advice
After Returning 어드바이스
타겟의 메서드가 정상적으로 실행된 이후(after) 시점에 처리해야 할 필요가 있는 부가기능을 정의한다.
-> Jointpoint 메서드 호출이 정상적으로 종료된 뒤에 실행되는 Advice
After Throwing 어드바이스
: 타겟의 메서드가 예외를 발생된 이후(after) 시점에 처리해야 할 필요가 있는 부가기능을 정의한다.
-> 예외가 던져 질때 실행되는 Advice
4. Advice를 정의하는 태그
<aop:before>
- 메서드 실행전에 적용되는 어드바이스를 정의한다.
<aop:after-returning>
- 메서드가 정상적으로 실행 된 후에 적용되는 어드바이스를 정의한다.
<aop:after-throwing>
- 메서드가 예외를 발생시킬 때 적용되는 어드바이스를 정의한다.
- try-catch 블록에서 catch 블록과 비슷하다.
<aop:after>
- 메서드가 정상적으로 실행되는지 또는 예외를 발생시키는지 여부에 상관없이 어드바이스를 정의한다.
- try-catch-finally에서 finally 블록과 비슷하다.
<aop:around>
- 메서드 호출 이전, 이후, 예외발생 등 모든 시점에 적용 가능한 어드바이스를 정의한다.
5. JointPoint 인터페이스
- JoinPoint는 Spring AOP 혹은 AspectJ에서 AOP가 적용되는 지점을 뜻한다.
- 해당 지점을 AspectJ에서 JoinPoint라는 인터페이스로 나타낸다.
6. JointPoint 메서드
| getArgs() | 메서드 아규먼트를 반환한다. |
| getThis() | 프록시 객체를 반환한다. |
| getTarget() | 대상 객체를 반환한다. |
| getSignature() | 어드바이즈 또는 메서드의 설명(description)을 반환한다. |
| toString() | 어드바이즈 되는 메서드의 설명을 출력한다. |
출처: https://shlee0882.tistory.com/206

메소드가 실행됐을 때 일어나는 흐름이 녹색 선이고 Join Point
메소드 끝났을 때 정상 실행 / 예외 발생.
이 Joinpoint를 Advice로 쪼개서 보는 그림.
모든 시점에서 동작하겠다는 게 Around (보통은 메소드 시작 전후)
PointCut : 적용 대상. 누구를 쪼개볼 건지
AOP 개념을 적용하면 핵심기능 코드 사이에 침투된 부가기능을 독립적인 Aspect로 구분해 낼수 있다.
구분된 부가기능 Aspect를 런타임 시에 필요한 위치에 동적으로 참여하게 할 수 있다.

servlet-context.xml 가면 AOP에 대한 내용 있음.

자바에서 쓰는 관점 지향 프로그램 - AspectJ
@Aspect : AOP용 클래스. AOP용 자바 라이브러리 안에 있음. 객체 생성 기능이 없어서 컴포넌트 추가로 달아주는 것.
@Component : 객체 생성. 외적인 용도의 클래스를 객체화 하려면 @컴포넌트 달아주면 스프링이 만들어준다.
@EnableAspectJAutoProxy : AspectJ 기능 활성화.
요즘은 xml을 굳이 쓰지 않고 추가적인 어노테이션만 기억하면 쓸 수 있음.
execution(접근권한 반환타입 범위)
반환타입 & 범위는 필수, 접근 권한은 옵션.
범위 형태를 잡기에는 execution이 좋다.
이해하기 쉬운 형태다.
- 범위 지정
execution : include 필터 (포함)
!execution : exclude 필터 (미포함)
* : 모든 것
*(..) : 모든 메소드
&& : and 필터
|| : or 필터

해석하면 -> 모든 반환타입으로 'com.spring.sample'의 모든 경로에서 'HomeController'를 찾고 그 안에 있는 모든 메소드가 적용대상(포인트컷)이다. 반환 타입 상관 없이 ~ 모든 메소드를 대상으로 한다고 설정하고 이름으로 testAOP를 붙여서 메소드 선언해준 것.
* 쓰면 like '%' 와 같음.

포인트컷을 메소드명(testAOP)으로 할당한 것.
joinPoint에 지점 전까지를 담고 있음?
해석하면 -> testAOP가 모든 시점을 만나면 그 아래를 실행하겠다는 것.
스프링이 도는 게 아니고 AspectJ라는 라이브러리가 따로 돌고 있기 때문에 수동으로 만들어줘야 한다.
그래서 리퀘스트를 통해 받아와야 하므로 리퀘스트 객체 취득을 위한 요청을 따로 별도로 해야 한다.

흐름을 끊어서 다른 데로 갈 수도 있다.
제 3의 선택지를 제공할 수 있다.
원래 AOPComponent
package com.spring.sample.common.component;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.ModelAndView;
@Aspect
@Component
@EnableAspectJAutoProxy
public class AOPComponent {
//Pointcut -> 적용범위
//@Pointcut(범위설정)
/*
* 범위
* execution -> include필터
* !execution -> exclude필터
* * -> 모든것
* *(..) -> 모든 메소드
* .. -> 모든 경로
* && -> 필터 추가
*/
@Pointcut("execution(* com.spring.sample..HomeController.*(..))")
public void testAOP() {} // pointcut의 이름과 같은 역할
//ProceedingJoinPoint -> 대상 적용 이벤트 필터
/*
* @Before -> 메소드 실행 전
* @After -> 메소드 실행 후
* @After-returning -> 메소드 정상실행 후
* @After-throwing -> 메소드 예외 발생 후
* @Around -> 모든 동작시점
*/
@Around("testAOP()")
public ModelAndView testAOP(ProceedingJoinPoint joinPoint)
throws Throwable {
ModelAndView mav = new ModelAndView();
//Request 객체 취득
HttpServletRequest request
= ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();
mav = (ModelAndView) joinPoint.proceed(); //기존 이벤트 처리 행위를 이어서 진행
System.out.println("------- testAOP 실행됨 ------");
return mav;
}
}
포인트컷 새로 추가.
포인트컷 여러 개 써도 문제 없다.
반환타입 다 줘도 상관 없음.
보통은 컨트롤러에만 건다.
메소드를 복수로 걸고 싶으면 .* 이렇게 모든 클래스라고 지정해줘도 됨.
@Pointcut("execution(* com.spring.sample..controller.ATController.*(..))")
우리는 모델앤뷰로 돌려주게 하고 있는데
ajax는 String을 반환하기 때문에 모델앤뷰로 구현이 안 돼서 문제가 된다.
메소드에 ajax나 action이 붙어 있어서 이걸 다 제외를 시켜줘야 한다.
얘네를 뺀 모든 메소드들이 로그인 처리 된다.
@Pointcut("execution(* com.spring.sample..controller.ATController.*(..))"
+ "")
마지막 따옴표 전에 엔터 치면 +"" 생김
@Pointcut("execution(* com.spring.sample..controller.ATController.*(..))"
+ "&& !execution(* com.spring.sample..controller.ATController.*Ajax(..))"
+ "&& !execution(* com.spring.sample..controller.ATController.*Action(..))")
근데 list도 detail도 로그인 없이 쓸 수 있다.(볼 수 있다)
action은 AOP 따로 만들어야 해서 빼도
어쩌구
그러면 제외해줄 것이 많아지니까 차라리 주소 치고 들어가는 insert와 update를 빼주자.
insert이면서 update인 메소드는 없으니 or을 씀. delete는 action에서 처리함.

@Pointcut("execution(* com.spring.sample..controller.ATController.*Insert(..))"
+ "|| execution(* com.spring.sample..controller.ATController.*Update(..))")
&&로 처리할 거 다 처리하고 ||로 제외할 거만 해야 한다.
섞어쓰면 관리 안 된다. 차라리 포인트컷 여러개 만드는 게 좋음.
메소드명은 인자가 다르기 때문에 똑같이 써도 됨.
예외 발생할 수 있으니 안전장치 걸어주고
@Pointcut("execution(* com.spring.sample..controller.ATController.*Insert(..))"
+ "|| execution(* com.spring.sample..controller.ATController.*Update(..))")
public void atAOP() {}
@Around("atAOP()")
public ModelAndView atAOP(ProceedingJoinPoint joinPoint) throws Throwable {
ModelAndView mav = new ModelAndView();
return mav;
}
세션을 가져와야 로그인 했는지 아닌지 확인할 수 있으니
AOPComponent에서 리퀘스트 객체 취득 코드 복사해서 가져오고
세션 가져오기
@Pointcut("execution(* com.spring.sample..controller.ATController.*Insert(..))"
+ "|| execution(* com.spring.sample..controller.ATController.*Delete(..))")
public void atAOP() {}
@Around("atAOP()")
public ModelAndView atAOP(ProceedingJoinPoint joinPoint) throws Throwable {
ModelAndView mav = new ModelAndView();
//Request 객체 취득
HttpServletRequest request
= ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();
HttpSession session = request.getSession();
return mav;
}
위에서 로그인할 때 쓰던 세션 필터 복사해와서 넣고
AOPComponent에서 기존 이벤트 처리 행위 이어서 진행하는 코드 복사해와서 넣기.
@Pointcut("execution(* com.spring.sample..controller.ATController.*Insert(..))"
+ "|| execution(* com.spring.sample..controller.ATController.*Delete(..))")
public void atAOP() {}
@Around("atAOP()")
public ModelAndView atAOP(ProceedingJoinPoint joinPoint) throws Throwable {
ModelAndView mav = new ModelAndView();
//Request 객체 취득
HttpServletRequest request
= ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();
HttpSession session = request.getSession();
//세션 필터 추가
if(session.getAttribute("sMemNm") != null
&& session.getAttribute("sMemNm") != "") { // 로그인 상태
mav = (ModelAndView) joinPoint.proceed(); //기존 이벤트 처리 행위를 이어서 진행
} else { // 비 로그인 상태
mav.setViewName("redirect:testALogin");
}
return mav;
}
ATController
insert에서 세션과 관련된 거 싹 빠지면서 간략해진다.
@RequestMapping(value = "/ATInsert")
public ModelAndView aTInsert(ModelAndView mav) {
mav.setViewName("testa/T/insert");
return mav;
}
ATInsert 주소 치고 들어가면 로그인 하라고 로그인 페이지 testALogin로 이동한다.
분명 세션과 관련된 처리가 없는데 세션과 관련된 처리를 한다.
내가 진행중이던 상황에 개입을 해서 별도 분할해서 쓰는 게 AOP의 핵심.
일반적으로 권한 체크, 로그인 체크 때 쓴다.
세션 적용할 것들이 더 필요하면 범위를 수정해주면 된다.
일일이 하기 힘드니까 한 번에 동시에 처리하려고 AOP 이용한다.
'학원 > 수업 기록' 카테고리의 다른 글
| 배치 프로그래밍 (Spring Scheduler) (0) | 2022.08.29 |
|---|---|
| TBOARD 수정, CATE 생성(카테고리) 실습 (0) | 2022.08.25 |
| [Spring] 댓글 게시판 (OB, 전체 코드) (0) | 2022.08.23 |
| 데이터베이스 어려운 문제.. 집계 함수에 대해 이해하기.. - OT (0) | 2022.07.15 |
| 생성자, main, 인자, 메소드, void, 반환값, 상속, Object 클래스, 오버라이딩 (0) | 2022.06.22 |