[Spring] AOP란?

코코코딩을 합시다·2024년 8월 21일

Spring CS

목록 보기
5/6
post-thumbnail

🛠️ AOP (Aspect-Oriented Programming)

AOP는 관점 지향 프로그래밍으로 관점을 기준으로 다양한 기능을 분리하여 보는 프로그래밍이다. 이 관점은 핵심 기능(비즈니스 로직)과 부가 기능을 나누는 관점을 뜻한다.

  • 핵심 관점 : 개발자가 적용하고자 하는 핵심 비즈니스 로직
  • 부가 관점 : DB 연결, 로깅, 보안, 트랜잭션 등 핵심 로직 이외의 기능

모든 메소드의 호출 시간을 측정하고 싶다면?

관점 지향 프로그래밍은 객체지향 프로그래밍을 보완하기 위해 등장한 개념으로 횡단 관심사(공통 관심 사항) 코드를 모듈화해 코드의 재사용성과 유지보수성을 높이는 것을 목적으로 한다.

📌 핵심 개념

  • Aspect

    • 공통(횡단) 관심사를 정의한 모듈이다.
  • Target

    • Aspect가 적용되는 곳 (클래스, 메소드..)
  • Advice

    • Aspect에서 실질적인 부가 기능을 담은 구현체
    • @Before : 메소드 실행 전
    • @AfterReturning : 메소드가 정상적으로 실행된 후
    • @AfterThrowing : 메소드 실행 중 예외 발생시
    • @After (Finally) : 정상 또는 예외와 상관 없이 메소드 실행 후 항상
    • @Around : 메소드 실행 전후 모두 제어, 가장 강력
  • Join Point

    • Advicerk Target에 적용되는 시점
    • 메서드 진입시, 생성자 호출시, 필드에서 값 꺼낼 때 등
  • Point Cut

    • Joint Point 중 Advice가 실행되는 지점
    • 특정 조건으로 JoinPoint를 필터링 (패키지 단위, 클래스 단위, 메서드 단위, 어노테이션 단위 ... )
  • Proxy

    • Spring AOP는 접근 제어 및 부가기능 추가를 위해 Proxy 객체를 사용한다.

Proxy 조금 더 자세히 보기

AOP는 Bean에만 적용될 수 있고, 따라서 객체의 역할로서만 존재하기 때문에 Aspect와 Target을 연결해주는 역할이 없다. 그 역할을 Proxy 클래스가 대신 해준다.
가짜 스프링 빈이라 생각해도 좋다.

분리된 핵심 기능과 부가 기능은 서로 양방향으로 알아야 할까?

아니다! 부가 기능을 담당한 쪽에서만 핵심 기능 정보를 알고, 사용해야 한다.

Proxy란타겟 객체의 메소드를 호출하기 전이나 후에 가로채 부가 작업을 수행할 수 있도록 하는 대리 객체이다.

  1. 프록시가 타겟 객체에 대한 메소드 호출을 가로채고,
  2. 어드바이스(로깅, 트랜잭션 처리 등)를 실행한 다음,
  3. 타겟 객체의 실제 메소드를 호출하고,
  4. 호출 후에 필요한 추가 작업을 수행한다.

프록시를 사용하면 부가 기능 제공 뿐만 아니라 클라이언트는 프록시 사용 여부를 모르고 타겟 객체를 사용하는 것처럼 느끼기 때문에 투명한 방식으로 공통 기능을 제공할 수 있다는 장점이 있다.

간단한 TimeTraceAspect 구현해보기

앞서 설명한 개념들을 적용한 TimeTraceAspect를 구현해보자 !

@Aspect
@Component
public class TimeTraceAspect {

    @Around("execution(* com.project.LIA..*(..))")
    public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        System.out.println("START: "+joinPoint.toString());
        try {
            return joinPoint.proceed();
        } finally {
            long finish = System.currentTimeMillis();
            long time = finish - start;
            System.out.println("END: " + joinPoint.toString() + " " + time + "ms");
        }
    }

}

  1. 기본적으로 AOP는 스프링 빈끼리만 가능하기 때문에 @Component로 빈 등록해준다.
  2. @Aspect를 붙여서 빈을 AOP 모듈로 등록해준다.
  3. Advice 어노테이션 중 선택해서 지정할 수 있다. 여기선 모든 메소드의 실행 전후 적용되는 강력한 어노테이션인 @Around를 붙여봤다.
  4. Around에서 범위를 지정해준다. ( com.project.LIA..(..))는 스프링 패키지 아래 모든 패키지에 적용된다는 뜻이다. (보통 패키지 레벨로 지정한다)

실행결과

START: execution(List com.project.LIA.service.BookServiceImpl.myList(String,Integer,Model))
START: execution(Page com.project.LIA.repository.BookRepository.findByUserUsername(String,Pageable))

END: execution(Page com.project.LIA.repository.BookRepository.findByUserUsername(String,Pageable)) 9ms
END: execution(List com.project.LIA.service.BookServiceImpl.myList(String,Integer,Model)) 9ms
END: execution(void com.project.LIA.controller.BookController.list(String,String,String,Integer,Model)) 17ms

LoggingAspect 구현해보기

AOP가 사용되는 대표적인 예시로 로깅이 있다.
간단한 로깅 처리를 Aspect의 다양한 어노테이션을 써보면서 AOP로 구현해보자 !

package com.project.LIA.aop;

@Aspect
@Component
public class LoggingAspect {
   private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);

   @Before("execution(* com.project.LIA.service..*(..))")
   public void logBefore() {
       logger.info("메소드 시행 전 ");
   }

   @AfterReturning("execution(* com.project.LIA.service..*(..))")
   public void logAfterReturning(){
       logger.info("메소드 정상 실행 후 ");
   }

   @AfterThrowing("execution(* com.project.LIA.service..*(..))")
   public void logAfterThrowing(){
       logger.info("");
   }

   @After("execution(* com.project.LIA.service..*(..))")
   public void logAfter(){
       logger.info("메소드 종료 후 성공 여부와 상관 없이 항상 실행 ");
   }

   @Around("execution(* com.project.LIA.service..*(..))")
   public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
       logger.info("메소드 실행 전");
       Object result;
       try {
           result = joinPoint.proceed(); // 실제 메소드 실행
       }catch (Exception e){
           logger.error("메소드 실행 중 예외 발생"+e.getMessage());
           throw e;
       }
       logger.info("메소드 실행 후 ");
       return result;
   }

}

메소드를 실행하면 다음과 같은 로그가 찍힌다.

2024-08-22T13:42:00.449+09:00  INFO 76834 --- [nio-8080-exec-6] com.project.LIA.aop.LoggingAspect        : 메소드 정상 실행 후 
2024-08-22T13:42:00.449+09:00  INFO 76834 --- [nio-8080-exec-6] com.project.LIA.aop.LoggingAspect        : 메소드 종료 후 성공 여부와 상관 없이 항상 실행 
2024-08-22T13:42:00.449+09:00  INFO 76834 --- [nio-8080-exec-6] com.project.LIA.aop.LoggingAspect        : 메소드 실행 후 
profile
좋아하는 걸로 밥 벌어먹기

0개의 댓글