TIL_20250207_AOP

Kim jisu·2025년 2월 7일

TIL

목록 보기
2/43

Spring AOP (Aspect Oriented Programming)

1. AOP란?

AOP는 관점 지향 프로그래밍으로, 어플리케이션의 핵심 비즈니스 로직과 공통 관심사(cross-cutting concerns)를 분리하여 모듈화하는 프로그래밍 패러다임입니다.

2. AOP 주요 용어

2.1 Aspect (관점)

  • 여러 클래스에 걸쳐 있는 공통 관심사를 모듈화한 것
  • 예: 로깅, 트랜잭션 관리, 보안 등

2.2 Join Point (조인포인트)

  • 프로그램 실행 중 Aspect를 적용할 수 있는 지점
  • Spring AOP에서는 메서드 실행 지점만 조인포인트로 사용
  • 예: 메서드 실행, 생성자 호출, 필드 값 변경 등

2.3 Pointcut (포인트컷)

  • 조인포인트 중에서 실제로 Aspect를 적용할 지점을 선별하는 표현식
  • 예: @Pointcut("execution(* com.example.service.*.*(..))")

2.4 Advice (어드바이스)

  • 특정 조인포인트에서 실행되는 코드
  • 실행 시점에 따라 다음과 같이 구분:
    • Before: 메서드 실행 전
    • After: 메서드 실행 후 (정상/예외 상관없이)
    • After Returning: 메서드가 정상적으로 반환된 후
    • After Throwing: 메서드에서 예외가 발생한 후
    • Around: 메서드 실행 전후

2.5 Target (타겟)

  • Aspect가 적용되는 대상 객체
  • 예: 트랜잭션 처리가 필요한 서비스 클래스

2.6 Weaving (위빙)

  • Aspect를 대상 객체에 연결하는 프로세스
  • 컴파일 시점, 클래스 로드 시점, 런타임 시점에 수행 가능
  • Spring AOP는 런타임 시점에 위빙 수행

3. Spring AOP 특징

  1. 프록시 기반 AOP 구현

    • Spring은 JDK 동적 프록시 또는 CGLIB를 사용하여 AOP 구현
    • 메서드 호출 시점에 위빙이 수행됨
  2. 메서드 조인포인트만 지원

    • 필드 접근, 생성자 호출 등은 지원하지 않음
    • 메서드 실행 지점만 조인포인트로 사용 가능

4. 사용 예시

API 사용 시간 측정 AOP

4.1 클래스 구조 및 설정

@Slf4j(topic = "UseTimeAop")
@Aspect
@Component
@RequiredArgsConstructor
public class UseTimeAop {
    private final ApiUseTimeRepository apiUseTimeRepository;
    // ...
}
  • @Aspect: 이 클래스가 AOP 클래스임을 명시
  • @Component: 스프링 빈으로 등록
  • @Slf4j: Lombok의 로깅 기능 사용
  • @RequiredArgsConstructor: 필수 필드에 대한 생성자 자동 생성

4.2 포인트컷 정의

@Pointcut("execution(* com.sparta.myselectshop.controller.ProductController.*(..))")
private void product() {}

@Pointcut("execution(* com.sparta.myselectshop.controller.FolderController.*(..))")
private void folder() {}

@Pointcut("execution(* com.sparta.myselectshop.naver.controller.NaverApiController.*(..))")
private void naver() {}
  • 세 개의 컨트롤러의 모든 메서드를 대상으로 포인트컷 정의
  • execution(* package.class.*(..)) 표현식 분석:
    • *: 모든 반환 타입
    • 패키지.클래스: 대상 클래스 경로
    • *: 모든 메서드
    • (..): 모든 파라미터

4.3 어드바이스 구현

@Around("product() || folder() || naver()")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
    // ...
}
  • @Around: 메서드 실행 전후를 모두 제어하는 어드바이스
  • 세 개의 포인트컷을 || 연산자로 결합
  • ProceedingJoinPoint: 원본 메서드 실행을 제어할 수 있는 객체

4.4 실행 시간 측정 로직

long startTime = System.currentTimeMillis();
try {
    Object output = joinPoint.proceed();
    return output;
} finally {
    long endTime = System.currentTimeMillis();
    long runTime = endTime - startTime;
    // ...
}
  • System.currentTimeMillis()로 시작/종료 시간 측정
  • joinPoint.proceed()로 원본 메서드 실행
  • finally 블록에서 실행 시간 계산

4.5 사용자 정보 처리

Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null && auth.getPrincipal().getClass() == UserDetailsImpl.class) {
    UserDetailsImpl userDetails = (UserDetailsImpl) auth.getPrincipal();
    User loginUser = userDetails.getUser();
    // ...
}
  • Spring Security의 컨텍스트에서 현재 인증 정보 조회
  • 로그인된 사용자만 처리하도록 체크

4.6 API 사용 시간 저장

ApiUseTime apiUseTime = apiUseTimeRepository.findByUser(loginUser).orElse(null);
if (apiUseTime == null) {
    apiUseTime = new ApiUseTime(loginUser, runTime);
} else {
    apiUseTime.addUseTime(runTime);
}
apiUseTimeRepository.save(apiUseTime);
  • 사용자별 API 사용 시간을 누적하여 저장
  • 최초 사용자는 새로운 기록 생성
  • 기존 사용자는 시간 누적

4.7 코드의 장점

  1. 관심사 분리

    • 실행 시간 측정 로직이 비즈니스 로직과 완전히 분리됨
    • 컨트롤러는 본연의 역할에만 집중 가능
  2. 재사용성

    • 여러 컨트롤러에 동일한 시간 측정 로직 적용
    • 포인트컷 추가만으로 다른 컨트롤러에도 적용 가능
  3. 유지보수성

    • 시간 측정 로직 변경 시 한 곳만 수정하면 됨
    • 비즈니스 로직 영향 없이 수정 가능

5. AOP 활용 사례

  1. 로깅 및 모니터링
  2. 트랜잭션 관리
  3. 보안 및 인증
  4. 예외 처리
  5. 성능 측정

오늘의 학습 포인트

  1. AOP는 공통 관심사를 분리하여 코드의 중복을 제거하고 유지보수성을 향상시킵니다.
  2. 어드바이스는 "언제" 실행할지를, 조인포인트는 "어디서" 실행할지를 결정합니다.
  3. Spring AOP는 프록시 기반으로 동작하며, 메서드 실행 지점만을 조인포인트로 지원합니다.
profile
Dreamer

0개의 댓글