반응형
해당 글에서는 Spring AOP에 대해 이해하고 환경설정을 해보는 방법에 대해서 공유를 목적으로 작성한 글입니다.
1) Spring AOP(Aspect-Oriented Programming, AOP)
1. AOP 용어 이해하기
💡 Spring AOP란?
- Spring AOP는 스프링 프레임워크에서 제공하는 기능 중 하나로 관점 지향 프로그래밍을 지원하는 기술입니다. Spring AOP는 로깅, 보안, 트랜잭션 관리 등과 같은 공통적인 관심사를 모듈화 하여 코드 중복을 줄이고 유지 보수성을 향상하는데 도움을 줍니다.
💡 관점 지향 프로그래밍(Aspect-Oriented Programming, AOP) 이란?
- 객체 지향 프로그래밍 패러다임을 보완하는 기술로 메소드나 객체의 기능을 핵심 관심사(Core Concern)와 공통 관심사(Cross-cutting Concern)로 나누어 프로그래밍하는 것을 말합니다. “핵심 관심사”는 각 객체가 가져야 할 본래의 기능이며, “공통 관심사”는 여러 객체에서 공통적으로 사용되는 코드를 말합니다.
- 여러 개의 클래스에서 반복해서 사용하는 코드가 있다면 해당 코드를 모듈화 하여 공통 관심사로 분리합니다. 이렇게 분리한 공통 관심사를 Aspect로 정의하고 Aspect를 적용할 메소드나 클래스에 Advice를 적용하여 공통 관심사와 핵심 관심사를 분리할 수 있습니다. 이렇게 AOP에서는 공통 관심사를 별도의 모듈로 분리하여 관리하며, 이를 통해 코드의 재사용성과 유지 보수성을 높일 수 있습니다.
[참고] 프로그래밍 패러다임과 관련하여 작성한 글 : https://adjh54.tistory.com/97
2. AOP 상세히 알아보기
💡 관점 지향 프로그래밍의 정의를 보면 핵심 관심사와 공통 관심사를 분리하여 프로그래밍하는 것을 의미합니다.
💡 여기서 3개의 A, B, C의 클래스가 있다고 가정합니다.
클래스 A에서는 주황, 파랑, 빨간색 블록으로 구성이 되어 있고 클래스 B에서는 빨강, 주황 블록으로 구성되어 있으며 클래스 C에서는 주황, 파랑 블록으로 구성되어 있습니다. 해당 색은 클래스 A, B, C에서 동일하게 사용되는 코드를 의미합니다.
예를 들어서 클래스 A에서 주황색 블록을 수정을 하게 되면 클래스 B, C에서도 수정을 해야 합니다. 이렇게 되면 유지보수 차원에서 모든 코드를 수정해야 하니 불편한 점이 있습니다. 그래서 Aspect X에서는 공통 관심사인 주황색 블록을 묶어서 모듈화를 시켜서 코드의 재 사용성과 유지 보수성을 강화하였습니다.
이렇듯, 관점 지향 프로그래밍에서는 소스코드에서 반복적으로 사용하는 코드를 하나로 묶어서 모듈화하여 재사용성과 유지 보수성을 높일 수 있는 강점을 가지고 있습니다.
2) Spring AOP 이해하기
1. 주요 용어 이해하기
용어 | 설명 |
Aspect | - 공통적인 기능들을 모듈화 한것을 의미합니다. |
Target | - Aspect가 적용될 대상을 의미하며 메소드, 클래스 등이 이에 해당 됩니다. |
Join point | - Aspect가 적용될 수 있는 시점을 의미하며 메소드 실행 전, 후 등이 될 수 있습니다. |
Advice | - Aspect의 기능을 정의한 것으로 메서드의 실행 전, 후, 예외 처리 발생 시 실행되는 코드를 의미합니다. |
Point cut | - Advice를 적용할 메소드의 범위를 지정하는 것을 의미합니다. |
2. 주요 어노테이션
메서드 | 설명 |
@Aspect | 해당 클래스를 Aspect로 사용하겠다는 것을 명시합니다. |
@Before | 대상 “메서드”가 실행되기 전에 Advice를 실행합니다. |
@AfterReturning | 대상 “메서드”가 정상적으로 실행되고 반환된 후에 Advice를 실행합니다. |
@AfterThrowing | 대상 “메서드에서 예외가 발생”했을 때 Advice를 실행합니다. |
@After | 대상 “메서드”가 실행된 후에 Advice를 실행합니다. |
@Around | 대상 “메서드” 실행 전, 후 또는 예외 발생 시에 Advice를 실행합니다. |
3) Spring AOP 환경설정하기
💡 해당 환경설정 및 예시는 Spring Boot 환경에서 특정 시점에 로그를 출력하는 예시를 기준으로 작성하였습니다.
1. 개발 환경
개발환경 | 버전 |
java | 11 |
Spring Boot | 2.7.5 |
Spring Framework | 5.7.4 |
Spring AOP | 2.7.5 |
Gradle | 7.5 |
개발툴 : IntelliJ | IntelliJ IDEA 2022.3 |
2. build.gradle내에 의존성을 추가합니다.
💡 Spring AOP를 의존성에 추가합니다.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-aop'
}
💡 Spring aop가 설치되었음을 확인하였습니다.
[참고] Spring AOP Maven Repository
Maven Repository: org.springframework.boot » spring-boot-starter-aop
3. 패키지와 파일을 구성합니다
💡 프로젝트 최상위에 ‘aspects’ 패키지를 구성하고 ‘LoggingAspect.java’ 파일을 생성하였습니다.
4. @Aspect 어노테이션을 추가하였습니다
💡 Spring AOP를 사용한다는 @Aspect 어노테이션을 추가하고 @Component 어노테이션을 함께 추가하였습니다.
package com.adjh.multiflexapi.aspects;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
/**
* 로깅 관련 AOP 구성
*
* @author : jonghoon
* @fileName : LoggingAspect
* @since : 2023/03/01
*/
@Aspect
@Component
public class LoggingAspect {
}
5. 주요 어노테이션에서 설명한 각각의 어노테이션을 기반으로 구성하였습니다.
💡 @Before, @After, @AfterReturning, @AfterThrowing, @Around와 같은 어노테이션을 사용하여 Advice를 구현합니다.
메서드 | 설명 |
@Before | 대상 “메서드”가 실행되기 전에 Advice를 실행합니다. |
@After | 대상 “메서드”가 실행된 후에 Advice를 실행합니다. |
@AfterReturning | 대상 “메서드”가 정상적으로 실행되고 반환된 후에 Advice를 실행합니다. |
@AfterThrowing | 대상 “메서드에서 예외가 발생”했을 때 Advice를 실행합니다. |
@Around | 대상 “메서드” 실행 전, 후 또는 예외 발생 시에 Advice를 실행합니다. |
[참고] 해당 부분에서는 Log4j2 구현체를 Slf4j 인터페이스를 이용하여 구성한 로깅을 사용하였습니다.
💡 해당 코드는 프로젝트 패키지에서 Controller 패키지 내에서 메서드가 수행되기 전, 후, 실행되어 반환된 후, 예외가 발생하였을 때에 해당 메서드가 수행되도록 처리가 되었습니다.
💡 그렇기에 Controller 패키지 내에서 매번 호출이 되었는지에 대한 로깅을 작성했던 부분에 대해서 로깅을 줄일 수 있는 이점이 생겼습니다.
package com.adjh.multiflexapi.aspects;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* 로깅 관련 AOP 구성
*
* @author : jonghoon
* @fileName : LoggingAspect
* @since : 2023/03/01
*/
@Aspect
@Component
@Slf4j
public class LoggingAspect {
/**
* Before: 대상 “메서드”가 실행되기 전에 Advice를 실행합니다.
*
* @param joinPoint
*/
@Before("execution(* com.adjh.multiflexapi.controller.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
log.info("Before: " + joinPoint.getSignature().getName());
}
/**
* After : 대상 “메서드”가 실행된 후에 Advice를 실행합니다.
*
* @param joinPoint
*/
@After("execution(* com.adjh.multiflexapi.controller.*.*(..))")
public void logAfter(JoinPoint joinPoint) {
log.info("After: " + joinPoint.getSignature().getName());
}
/**
* AfterReturning: 대상 “메서드”가 정상적으로 실행되고 반환된 후에 Advice를 실행합니다.
*
* @param joinPoint
* @param result
*/
@AfterReturning(pointcut = "execution(* com.adjh.multiflexapi.controller.*.*(..))", returning = "result")
public void logAfterReturning(JoinPoint joinPoint, Object result) {
log.info("AfterReturning: " + joinPoint.getSignature().getName() + " result: " + result);
}
/**
* AfterThrowing: 대상 “메서드에서 예외가 발생”했을 때 Advice를 실행합니다.
*
* @param joinPoint
* @param e
*/
@AfterThrowing(pointcut = "execution(* com.adjh.multiflexapi.controller.*.*(..))", throwing = "e")
public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {
log.info("AfterThrowing: " + joinPoint.getSignature().getName() + " exception: " + e.getMessage());
}
/**
* Around : 대상 “메서드” 실행 전, 후 또는 예외 발생 시에 Advice를 실행합니다.
*
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("execution(* com.adjh.multiflexapi.controller.*.*(..))")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("Around before: " + joinPoint.getSignature().getName());
Object result = joinPoint.proceed();
log.info("Around after: " + joinPoint.getSignature().getName());
return result;
}
}
[ 더 알아보기 ]
💡 @EnableAspectJAutoProxy 어노테이션
- 스프링 컨텍스트 내에서 AspectJ AOP 프레임워크를 사용할 수 있도록 하는 어노테이션입니다. 해당 어노테이션을 사용하면 AOP 프록시 빈을 자동으로 등록하고 AOP를 사용할 수 있게 됩니다. Spring Boot에서는 @EnableAspectJAutoProxy 어노테이션을 사용하지 않아도 AspectJ AOP 프레임워크를 사용할 수 있습니다. 이는 Spring Boot가 자동으로 AOP 프록시 빈을 등록하고 AOP를 사용할 수 있게 해 주기 때문입니다.
[참고] JoinPoint 인터페이스의 메서드 설명
메서드 | 설명 |
getArgs() | 대상 메서드의 인자 목록을 반환합니다. |
getSignature() | 대상 메서드의 정보를 반환합니다. |
getSourceLocation() | 대상 메서드가 선언된 위치를 반환합니다. |
getKind() | Advice의 종류를 반환합니다. |
getStaticPart() | Advice가 실행될 JoinPoint의 정적 정보를 반환합니다. |
getThis() | 대상 객체를 반환합니다. |
getTarget() | 대상 객체를 반환합니다. |
toString() | JoinPoint의 정보를 문자열로 반환합니다. |
toShortString() | JoinPoint의 간단한 정보를 문자열로 반환합니다. |
toLongString() | JoinPoint의 자세한 정보를 문자열로 반환합니다. |
6. 결과 확인
1. Controller로 호출이 되는 사용자 리스트를 조회하는 API 호출을 수행하였습니다.
2. 서버의 콘솔을 확인합니다.
💡 해당 콘솔을 확인하면 총 4번의 구성한 LogginAspect.java 부분에 로깅이 출력되었습니다.
1. @Around: Around before로 메서드가 실행되기 전에 수행되는 부분이 출력되었습니다.
2. @Before: Before로 메서드가 실행되기 전에 수행되는 부분이 출력되었습니다.
3. @AfterReturning: AfterReturning로 메서드가 정상적으로 수행된 뒤에 메서드 명과 결과값이 출력이 되었습니다.
4. @After: After로 메서드가 실행된 후에 결과값이 출력되었습니다.
오늘도 감사합니다. 😀
반응형
'Java > Spring Boot' 카테고리의 다른 글
[Java] Spring Boot Cache 이해하고 설정하기 -2 : 사용 및 활용 예시 (0) | 2023.04.16 |
---|---|
[Java] Spring Boot Cache 이해하고 설정하기 -1 : 정의, 환경 설정 (2) | 2023.04.16 |
[Java] Spring Boot Tomcat Access Log 이해하고 설정하기 (0) | 2023.02.26 |
[Java] Spring Boot Security 이해하기 -4: JWT 환경 설정 및 구성 하기 (4) | 2023.01.01 |
[Java] Spring Boot Security 이해하기 -3: JWT(JSON Web Token) 이해하기 (0) | 2022.12.21 |