728x90
AOP란?
AOP는 관점 지향 프로그래밍이라고 불리며, 핵심 로직과는 별개로 반복되는 공통 관심사를 분리해서 관리할 수 있게 해주는 프로그래밍 패러다임
대표적인 공통 관심사 예시 : 로깅, 트랜잭션 처리, 보안 체크, 성능 측정
❓ 왜 AOP를 쓸까?
기존 코드에 이런 공통 기능들을 일일이 추가하면 코드가 지저분해지고, 유지보수도 어려워진다.
AOP를 쓰면 이런 기능들을 한 곳에서 정의하고, 필요한 곳에 자동으로 적용할 수 있어서 코드가 더 깔끔하고 재사용성도 높아진다.
=> 여러 클래스에 공통적으로 작업을 적용할 때 사용한다.
✅ 핵심 개념 간단 정리
- Aspect: 공통 관심사를 모듈화한 것 (예: 로깅 기능)
- Join Point: Aspect를 적용할 수 있는 지점 (예: 메소드 호출 시점)
- Advice: 언제, 어떤 방식으로 공통 기능을 적용할지 정의 (예: 메소드 실행 전/후)
- Pointcut: Advice가 적용될 Join Point들을 지정
- Weaving: 실제 코드에 Aspect를 적용하는 과정
AOP 간단 구현 (로깅)
build.gradle
AOP 라이브러리 추가
implementation 'org.springframework.boot:spring-boot-starter-aop'
@Aspect 어노테이션
Aspect 어노테이션은 해당 클래스에 공통 관심사가 들어있다는 걸 스프링에게 알려주는 역할
-> 흩어진 관심사(로그 체크, 시간 체크 등)를 하나로 묶은 것 (모듈화)
Pointcut
- AOP를 적용할 대상을 정의
- "execution(* com.example.logging..*.*(..))" : com.example.logging 패키지 및 하위 패키지의 모든 클래스의 모든 메소드를 대상으로 함
Before
- 대상 메소드가 실행되기 전에 이 메소드(before)가 실행됨
- 비즈니스 메소드 전 공통 동작을 삽입하는 것
- @Before("pointcut()") : pointcut 메소드 실행 전에 이 메소드 실행
Join Point
- 특정 기능이 실행될 위치나 시점을 의미
- 현재 실행되는 메소드, 인자, 클래스 등의 정보를 담고 있음
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Aspect // 흩어진 관심사(부가 기능)를 하나로 묶은 것 (모듈화)
@Component
public class SimpleAop {
private final Logger log = LoggerFactory.getLogger(getClass());
@Pointcut("execution(* com.example.logging..*.*(..))")
private void pointcut() {
log.info("pointcut");
}
@Before("pointcut()")
public void before(JoinPoint joinPoint) { // Join Point : 특정 기능이 실행될 위치나 시점을 의미
log.info("before");
}
}
AOP 사용 (Service & Controller)
위에서 설정한대로 before 메소드가 실행되려면 com.example.logging 패키지 안에 Service 파일과 Controller 파일이 포함되어 있어야 한다.
Service
@Service
public class AbcService {
private final Logger log = LoggerFactory.getLogger(getClass());
public void test() {
log.info("서비스 실행됨");
}
}
Controller
@RequiredArgsConstructor
@RestController
@RequestMapping("/abc")
public class AbcController {
private final Logger log = LoggerFactory.getLogger(getClass());
private final AbcService abcService;
@GetMapping("/test")
public ResponseEntity<String> test() {
log.info("정상"); // 정상일 때
log.trace("trace : 동작 상황을 모두 출력(메소드 실행, 종료, 반복문 실행 등)");
log.debug("debug : 동작 상황을 모두 출력(로직, 변수 값 등)");
log.warn("warn : 잘못이 될 수도 있고 아닐 수도 있는 것");
log.error("에러"); // 에러일 때
abcService.test();
return ResponseEntity.ok("test");
}
}
실행 흐름
1. 브라우저에서 /abc/test 요청
2. AbcController.test() 실행
3. 이 안에서 abcService.test() 호출
4. abcService.test() 실행 직전에 → AOP의 @Before 메서드 실행됨
5. 그 다음에 실제 abcService.test() 실행됨
before에서 메소드 받아오기
클래스 이름과 메소드 이름 가져오기
@Before("pointcut()")
public void before(JoinPoint joinPoint) {
String className = joinPoint.getSignature().getDeclaringTypeName();
String methodName = ((MethodSignature) joinPoint.getSignature()).getMethod().getName();
log.info("{} - {} - 실행되기 전", className, methodName);
}
실행 시간 체크
@Around("pointcut()") // 실행되기 전, 후 다 포함
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
StopWatch stopWatch = new StopWatch();
String className = joinPoint.getSignature().getDeclaringTypeName();
String methodName = ((MethodSignature) joinPoint.getSignature()).getMethod().getName();
try {
stopWatch.start();
return joinPoint.proceed();
} finally {
stopWatch.stop();
log.info("{} - {} - {}ms", className, methodName, stopWatch.getTotalTimeMillis());
}
}
728x90
'BE > Spring Boot' 카테고리의 다른 글
[Spring Boot] Logback이란? / 로그 서버 구성, 연동 (0) | 2025.04.12 |
---|---|
[Spring Boot] 로그(Logging) (0) | 2025.03.29 |
[Spring Boot] CQRS (0) | 2025.03.29 |
[Spring Boot] Gateway에서 인가 처리 (Filter) (0) | 2025.03.21 |
[Spring Boot] Eureka란? / 스프링 부트에서 Eureka, Gateway 설정 (0) | 2025.03.19 |