오늘은 몰랐으면 내일은 알면 된다
2022-12-16 (2) AOP 본문
AOP는 관점 지향 프로그래밍(Aspect Oriented Programming)이며, 이때의 관점(Aspect)이라는 것은 개발자들에게는 관심사(Concern)라는 말로 통용된다. 또 관심사란, 개발 시 필요한 고민이나 염두에 두어야 하는 일이라고 생각할 수 있다.
- 파라미터가 제대로 들어왔는가?
- 올바른 권한을 가진 사용자인가?
- 예외는 어떻게 처리할 것인가?
위와같은 핵심 로직은 아니지만 필요한 관심사들은 AOP를 적용하지 않은 상태에서라면 반복적으로 코드에 반영하게 된다. (반복적으로 등장하는 try catch 등)
AOP가 추구하는 것은 관심사의 분리이다. 개발자가 염두에 두어야하는 일들은 별도의 관심사로 분리하고, 핵심 비즈니스 로직만을 작성할 것을 권장한다.
더 간단히 생각해보자면, 관심사는 '주변 로직'이라고 할 수 있겠다. 사칙연산을 한다고 치면, '핵심 로직'은 숫자를 연산하는 것이지만, '주변 로직'은 0으로 나누는 것인지 아닌지 등을 체크하는 것이라고 볼 수 있다.
즉, 관심사는 가장 중요한 로직은 아니지만 필요한 사전 조건이나 사후 조건 등이라고 간주할 수 있다.
과거에 비즈니스 로직을 작성하면서 내부에 필요한 관심사를 처리하던 방식과 정반대의 접근 방식이라고 볼 수 있는데,
AOP는 과거에 개발자가 작성했던 '관심사 + 비즈니스 로직'을 분리해서 별도의 코드로 작성하도록 하고, 실행할 때 이를 결합하는 방식으로 접근한다. (컴파일 혹은 실행 시점에 결합)
실제 실행할 때는 결합된 상태의 코드가 실행되기 때문에, 개발자들은 핵심 비즈니스 로직에만 근거해서 코드를 작성하고, 나머지는 어떤 관심사들과 결합할 것인지 설정하는 것만으로 모든 개발을 마칠 수 있다.

[AOP 용어]

AOP를 적용한다는 것은 기존의 코드를 수정하지 않고도 원하는 관심사들(cross-concern)을 엮을 수 있다는 것이다.
Target은 개발자가 작성한 핵심 비즈니스 로직을 가지는 객체이다. 순수한 비즈니스 로직을 의미하고, 어떠한 관심사들과도 관계를 맺지 않는다. 순수한 코어(Core)라고 볼 수 있다.
Proxy는 내부적으로 Target을 호출하지만, 중간에 필요한 관심사들을 거쳐서 Target을 호출하도록 자동 혹은 수동으로 작성된다. 대부분의 경우에는 자동으로 생성되는 auto-proxy 방식을 이용한다.
JoinPoint는 Target 객체가 가진 여러 메소드이다. (스프링 AOP에서는 메소드만 JoinPoint가 될 수 있다.)
외부에서의 호출은 Proxy 객체를 통해서 Target 객체의 JoinPoint를 호출하는 방식이라고 이해할 수 있다.
Target에는 여러 메소드가 존재하기 때문에, 어떤 메소드에 어떤 관심사를 결합할 것인지를 결정해야 하는데, 이 결정을 Pointcut 이라고 한다.
Pointcut은 관심사와 비즈니스 로직이 결합되는 지점을 결정하는 것이다.
Advice는 실제로 관심사(Aspect)를 구현한 코드를 의미한다. Advice는 동작 위치에 따라 다음과 같이 구분된다.
| 구분 | 설명 |
| Before Advice | Target의 JoinPoint를 호출하기 전에 실행되며, 코드의 실행 자체에는 관여할 수 없다. |
| After Returning Advice | 모든 실행이 정상적으로 이루어진 후에 동작하는 코드이다. |
| After Throwing Advice | 예외가 발생한 뒤에 동작하는 코드이다. |
| After Advice | 정상적으로 실행되거나 예외가 발생했을 때 구분없이 실행되는 코드이다. |
| Around Advice | 메소드의 실행 자체를 제어할 수 있는 가장 강력한 코드이다. 직접 대상 메소드를 호출하고 결과나 예외를 처리할 수 있다. |
Pointcut은 Advice를 어떤 JoinPoint에 결합할 것인지를 결정하는 설정이다. AOP에서 Target은 결과적으로 Pointcut에 의해 자신에게 없는 기능들을 가지게 된다.
Pointcut에서 주로 사용되는 설정은 다음과 같다.
| 구분 | 설명 |
| execution(@execution) | 메소드를 기준으로 Pointcut을 설정한다. |
| within(@within) | 특정한 타입(클래스)을 기준으로 Pointcut을 설정한다. |
| this | 주어진 인터페이스를 구현한 객체를 대상으로 Pointcut을 설정한다. |
| args(@args) | 특정한 파라미터를 가지는 대상들만을 Pointcut으로 설정한다. |
| @annotation | 특정한 어노테이션이 적용된 대상들만을 Pointcut으로 설정한다. |
구체적인 예시를 한번 살펴보자. 예를들어 문을 열고, 뭔가를 넣고, 닫는다.
A클래스에서는 창문을 열고, B클래스에서는 강의장의 문을열고, C클래스에서는 냉장고의 문을 연다고 하겠다.
이 때, '연다' 등의 여러 핵심 로직(JoinPoint)을 '논리적'으로 묶어놓은 것을 Pointcut이라고 한다.
또한 어떠한 부가로직을 어떠한 핵심로직에 적용할 것인지 정의한 것을 Advice라고 한다.
그리고 이러한 Pointcut과 Advice를 합친것을 Advisor(스프링 빈이다)라고 한다.
Weaving이란 Pointcut에 Advice를 삽입하는 것이다.

[Weaving 방식]
| 종류 | 설명 |
| Weaving | - Advice를 Pointcut에 삽입하는 것 |
| 컴파일시에 Weaving | - AspectJ에서 사용하는 방식 - 컴파일할 때 알맞는 위치에 공통코드를 삽입 |
| 클래스로딩시에 Weaving (LoadTimeWeaving) | - AspectJ에서 사용하는 방식 - 로딩한 클래스의 바이너리 정보를 변경하여 알맞은 위치에 공통코드를 삽입한 새로운 바이너리 코드를 사용한다. - 원본클래스파일은 변경하지 않는다. |
| 런타임시에 Weaving (동적 AOP) | - 프록시를 생성하여 핵심로직을 구현한 객체에 접근하게 된다. |
[프록시 패턴]
: 프록시프록시하는데 프록시는 뭘까? 쉽게말하면 대리자라고 보면 되겠다.
직접적으로 객체를 사용하는 것이 아니라, 해당 객체의 대리자를 만들어서 사용자들은 대리자를 사용하게 하는 것이다.
프록시 객체에서는 원래 객체를 감싸서 원하는 부가로직을 원하는 위치에 구현할 수 있다.

[Advice 작성]
@Aspect
@Log4j
@Component
public class LogAdvice {
@Before("execution(* org.zerok.service.SampleService*.*(..))")
public void logBefore() {
log.info("로그로그");
}
}

@Aspect는 해당 클래스의 객체가 Aspect를 구현한 것임을 나타내기 위해서 사용한다.
@Component는 스프링 빈으로 관리되기 위한 어노테이션이다.
@Before는 위에서 정리한 Advice의 종류 중, BeforeAdvice를 구현한 메소드라는 뜻이다.(@After, @AfterReturning, @AfterThrowing, @Around)
Advice와 관련된 어노테이션들은 내부적으로 Pointcut을 지정한다. @Before 내부의 문자열은 AspectJ의 표현식이다.
'Java > JAVA 개발자 양성과정' 카테고리의 다른 글
| 2022-12-16 (4) Dynamic SQL을 이용한 실습 (0) | 2022.12.16 |
|---|---|
| 2022-12-16 (3) Transaction (0) | 2022.12.16 |
| 2022-12-16 (1) MyBatis 동적 SQL (0) | 2022.12.16 |
| 2022-12-15 (4) REST (0) | 2022.12.15 |
| 2022-12-15 (3) 스프링 MVC 프로젝트의 기본 구성 (0) | 2022.12.15 |