[SpringBoot] 스프링 AOP (Spring AOP)

해니·2023년 12월 7일
0

SpringBoot

목록 보기
3/24
post-thumbnail

AOP란?

  • Aspect Oriented Programming의 약자로 관점 지향 프로그래밍이라고 불린다.
  • 어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어서 보고 그 관점을 기준으로 각각 모듈화한다.
    • 핵심적인 관점은 핵심 비즈니스 로직을 의미한다.
    • 부가적인 관점은 앞에서 말씀드린 공통으로 처리되어야 하는 코드를 의미한다.
      ex) 로그 출력, 보안 처리, 예외 처리 등
  • 모든 클래스 및 메소드에서 자주 등장할 중복되는 관심사와 코드를 별도로 분리하고 모듈화를 진행함으로써 중복되는 코드를 줄이고 가독성을 높이는 방법이다.


흩어진 관심사 (Crosscutting Concerns)

  • 소스 코드상에서 다른 부분에 계속 반복해서 쓰는 코드들을 의미한다.
  • AOP 는 이렇게 흩어진 관심사를 Aspect 를 이용해서 해결한다.

💡 흩어진 관심사를 Aspect로 모듈화하고 핵심적인 비즈니스 로직에서 분리하여 재사용하겠다는 것이 AOP의 취지다!



AOP 용어

  • 관점(Aspect)

    • 흩어진 관심사를 모듈화 한 것. 주로 부가기능을 모듈화함.
    • 부가적인 기능을 정의한 코드인 어드바이스와, 어드바이스를 어느 곳에 적용할지 결정하는 포인트컷의 조합으로 만들어진다.
  • 어드바이스(Advice)

    • 실질적으로 어떤 일을 해야할 지에 대한 것
    • 실질적인 부가기능을 담은 구현체
  • 조인 포인트(Join point)

    • Advice가 적용될 위치, 끼어들 수 있는 지점.
    • 메서드 진입 지점, 생성자 호출 시점, 필드에서 값을 꺼내올 때 등 다양한 시점에 적용가능
  • 포인트컷(Pointcut)

    • JointPoint의 상세한 스펙을 정의한 것.
    • 'A란 메서드의 진입 시점에 호출할 것'과 같이 더욱 구체적으로 Advice가 실행될 지점을 정할 수 있음
  • 타겟(Target)

    • Aspect를 적용하는 곳 (클래스, 메서드 .. )
  • 프록시(Proxy)

    • Advice가 적용되었을 때 생성되는 객체



AOP 구현방법 3가지

  • 컴파일 시점에 코드에 공통 기능을 삽입한다.
  • 클래스 로딩 시점에 바이트 코드에 공통 기능을 삽입한다.
  • 런타임에 프록시 객체를 생성해서 공통 기능을 삽입한다. (프록시패턴)



프록시 패턴(Proxy Pattern)

  • 실제 기능을 수행하는 객체 대신 가상의 객체(proxy object)를 사용해 로직의 흐름을 제어하는 디자인 패턴.
    • 프록시 객체는 원래 객체를 감싸고 있는 객체이다. (원래 객체와 타입은 동일)
    • 프록시 객체가 원래 객체를 감싸서 client의 요청을 처리하게 하는 패턴이다.
  • 프록시 패턴 특징
    • 원래 하려던 기능을 수행하며 그외의 부가적인 작업(로깅, 인증, 네트워크 통신 등)을 수행하기에 좋다.
    • 비용이 많이 드는 연산(DB 쿼리, 대용량 텍스트 파일 등)을 실제로 필요한 시점에 수행할 수 있다.
    • 사용자 입장에서는 프록시 객체나 실제 객체나 사용법은 유사하므로 사용성이 좋다.

예제 코드


// 출처: 

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.thymeleaf.util.StringUtils;

@Slf4j
@Aspect
@Component
public class LoggerAspect {

    @Around("execution(* com.study.domain..*Controller.*(..)) || execution(* com.study.domain..*Service.*(..)) || execution(* com.study.domain..*Mapper.*(..))")
    public Object printLog(ProceedingJoinPoint joinPoint) throws Throwable {

        String name = joinPoint.getSignature().getDeclaringTypeName();
        String type =
                StringUtils.contains(name, "Controller") ? "Controller ===> " :
                StringUtils.contains(name, "Service") ? "Service ===> " :
                StringUtils.contains(name, "Mapper") ? "Mapper ===> " :
                "";

        log.debug(type + name + "." + joinPoint.getSignature().getName() + "()");
        return joinPoint.proceed();
    }

}

출처:: https://congsong.tistory.com/25 [Let's develop:티스토리]


@Component : 개발자가 직접 정의한 클래스를 빈(Bean)으로 등록할 때 사용
@Aspect : AOP 기능을 하는 클래스의 클래스 레벨에 선언하는 어노테이션


어드바이스(Advice)

  • @Before : Target 메서드 호출 이전에 적용할 어드바이스 정의

  • @AfterReturning : Target 메서드가 성공적으로 실행되고, 결괏값을 반환한 뒤에 적용

  • @AfterThrowing : Target 메서드에서 예외 발생 이후에 적용 (try/catch의 catch와 유사)

  • @After : Target 메서드에서 예외 발생에 관계없이 적용 (try/catch의 finally와 유사)

  • @Around : Target 메서드 호출 이전과 이후 모두 적용 (가장 광범위
    하게 사용됨)


execution 포인트컷

  • * com.study.domain..*Controller.*(..)
    :com.study.domain의 모든 하위 패키지 중 xxxController와 같은 패턴의 이름을 가진 클래스에서 파라미터가 0개 이상인 메서드를 의미
  • * com.study.domain..*Service.*(..)
    :com.study.domain의 모든 하위 패키지 중 xxxService와 같은 패턴의 이름을 가진 클래스에서 파라미터가 0개 이상인 메서드를 의미
  • * com.study.domain..*Mapper.*(..)
    :com.study.domain의 모든 하위 패키지 중 xxxMapper와 같은 패턴의 이름을 가진 인터페이스에서 파라미터가 0개 이상인 메서드를 의미




출처
[Spring] 스프링 AOP (Spring AOP) 총정리 : 개념, 프록시 기반 AOP, @AOP
Spring - AOP 기본개념 및 주요 기능, 예제와 함께 이해하기
스프링 부트(Spring Boot) - AOP와 트랜잭션(Transaction)
스프링 - AOP
[스프링] AOP 와 프록시 패턴
[Spring] 프록시 패턴 & 스프링 AOP

profile
💻 ⚾️ 🐻

0개의 댓글