프로젝트에서 @Async를 적용 시켜보았다.
@Async는 Spring AOP를 기반으로 동작하게 되는데, Spring AOP를 알아보자.
🎯 관점 지향 프로그래밍
웹 개발은 보통 계층적인 구조로 나누어져 있다.
- Web Layer - json변환, 뷰 반환
- Business Layer - 비즈니스 로직
- Data Layer - persistence 로직
이런 계층들에 공통적으로 적용되는 부분들이 있다. 바로 보안, 성능측정, 로깅과 같은 부분들이다.
각 계층에서 핵심적인 부분들은 아니지만 계층을 걸쳐 공통적으로 존재하여 공통관심사라고 불린다.
이러한 공통 관심사를 관리해주기 위해 AOP(관점 지향 프로그래밍)이 등장하였다.
AOP가 동작하는 흐름은 크게 아래와 같다.
- 공통 관심사항을 Aspect로 만든다.
- 로깅, 보안, 성능 모두 하나의 Aspect이다.
- PointCut을 정의한다.
- Aspect를 어디에 적용할 것인지를 명시하는 로직을 정의
Java진영에서 AOP를 위한 프레임워크에는 SpringAOP와 AspectJ가 있다.
🎯 스프링 AOP 용어 정리하기
컴파일 시점 용어
- Advice : 실행 하려는 코드를 의미한다.
- ex) logger.info("Before Aspect - Method is called - {}", joinPoint);
- Pointcut : 인터셉터 하려는 메서드 호출에 대한 표현식
- ex) execution(* org.example.service..(..))
- Aspect : Advice와 Pointcut의 조합
- Advice : 무엇을 할 것인가.
- Pointcut : 언제 메서드 호출을 인터셉트 할 것인가.
- Weaver : AOP를 구현한 프레임워크를 의미한다.
- AspectJ나 Spring AOP도 위버이다.
- 위의 프레임워크가 하는 AOP를 실행하는 과정을 통틀어 위빙이라 한다.
- AspectJ나 Spring AOP도 위버이다.
실제로 프로젝트에 로깅위해 간단히 적용해 보았다 👇 (자세한 사용법은 슉슉 찾아서 적용 해보자!)
package yjh.devtoon.common.aop;
import org.aspectj.lang.annotation.Pointcut;
public class PointcutConfig {
/**
* presentation 계층의 모든 메서드에 적용하는 포인트컷 경로.
*/
@Pointcut("execution(* yjh.devtoon.*.presentation.*.*(..))")
public void presentationPackageConfig() {
}
}
package yjh.devtoon.common.aop;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
@Slf4j
@Aspect
@Configuration
public class LoggingAspect {
@AfterReturning("yjh.devtoon.common.aop.PointcutConfig.presentationPackageConfig()")
public void logMethodCall(final JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
String className = signature.getDeclaringType().getSimpleName();
String methodName = signature.getName();
HttpServletRequest request =
((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
log.info("Request: {} {} ({}), {}.class -> {}()", request.getMethod(), request.getRequestURI(), request.getRemoteHost(),
className, methodName);
}
}
런타임 시점 용어
- Join Point : Pointcut 조건이 참이면 Advice가 실행되는데, Advice가 실행되는 인스턴스를 JoinPoint 라고 한다. 메서드 실행 관련 필요한 여러 정보를 담고있다.
유용한 어노테이션
- @Before : 메서드가 호출되기 전 로그를 출력하거나 인증이 필요할 때 유용하다.
- @After : 메서드가 실행되고 난 뒤 수행할 작업을 지정한다.
- 메서드가 성공하든 실패든 익셉션을 던지든 상관없이 실행
- @AfterReturning : 오직 메서드가 성공했을 경우에서 실행, 익셉션 발생하면 실행하지 않음
- @Around : 메서드 실행 전과 후에 동작한다.
🎯 스프링 AOP 동작방식
스프링에서는 사용할 Bean(객체)를 생성하고, 컨테이너에 등록하는 방식으로 동작한다.
스프링에서 AOP를 동작시키는 원리는 아래와 같다.
1. Bean으로 등록된 객체를 생성한 뒤 후처리기에 전달한다.
2. 빈 후처리기는 포인트컷을 확인하여 전달받은 빈이 프록시 적용 대상이지 확인한다.
3. 프록시 생성 대상 Bean이라면 프록시 객체로 대체하여 빈 후처리기가 컨테이너에 등록한다.
즉, Bean을 생성하여 컨테이너에 등록하는 시점에 AOP로 활용할 객체인지를 판단하여, 일단 객체 대신 프록시 객체로 대체해서 컨테이너에 등록하는게 핵심이다.
앞서 사용했던 @Async도 스프링 AOP처럼 @Async가 붙은 메서드의 클래스는 일단 객체(Bean)가 아니라 프록시 객체로 스프링 컨테이너에 등록된 것이다.
이를 통해서 @Async가 붙은 메서드에는 스레드풀에서 스레드를 할당받아서 별도로 비동기 방식으로 동작시킬 수 있었다.
🎯 더 알아보기
스프링 AOP동작방식을 간단히 알아보았는데, 이번에는 빈 후처리기와 프록시 객체라는 새로 알게된 개념이 등장하였다.
몰랐던 개념이 등장했기 때문에 조금 더 짚고 넘어가야 될 것 같다.
<계속해서 학습할 내용>
- 프록시 객체
- 빈 후처리기
'프로젝트 > 데브툰' 카테고리의 다른 글
[데브툰] CI 적용하기 (0) | 2024.07.16 |
---|---|
[프로젝트] @Async를 알아보자(feat. 빈 후처리기) (0) | 2024.05.08 |
[프로젝트] @Async를 알아보자(feat. Spring과 Proxy) (0) | 2024.05.07 |
[프로젝트] 비동기 프로그래밍 적용해보기 (0) | 2024.05.01 |
[프로젝트] 프로젝트 목표 및 기획 (0) | 2024.04.30 |