티스토리 뷰

AOP (Aspect Oriented Programming)

들어가며

공통 관심사항(cross cutting concern) vs 핵심 관심사항(core concern) 분리

  • 핵심 기능에 부가 기능(ex. 호출 시간 측정, 트랜잭션)을 추가했다. 핵심 기능을 담당하는 코드에서 흩어져 나타나는 부가 기능 코드는, 말 그대로 부가적이기 때문에 적용이 되는 대상(Target)이 있어야 의미가 있다. 두 기능을 무조건 분리하기가 애매하다.

  • 부가 기능을 여러 메소드에서 공통으로 사용한다고 해보자. 부가 기능 코드가 중복된다.

  • 요구사항이 변경되서 부가 기능 코드를 수정해야 한다. 여러 메소드에서 사용한 부가 기능 코드를 일일히 수정해야 한다.

위 상황에서 하나의 관심, 핵심 비즈니스 로직에만 집중하는 단일 책임의 원칙을 지키는 건 어렵다. 반복되는 부가 기능 코드로 인해 추후 유지보수도 힘들다.

핵심 기능을 담당하는 코드는 해당 기능에만 관심을 갖도록 하고, 부가기능은 따로 모듈화해서 적용이 되어야 할 때만 적용할 수 없을까?

이걸 가능하게 하는 기법이 AOP다.

1. AOP 주요 개념과 용어

AOP?

  • 애플리케이션의 핵심적인 기능에서 부가적인 기능을 분리해서 애스펙트라는 독특한 모듈로 만들어서 설계하고 개발하는 방법이다.
  • AOP는 OOP를 돕는 보조적인 기술이지 OOP를 완전히 대체하는 새로운 개념은 아니다.
    • 애스펙트를 분리함으로써 핵심기능을 설계하고 구현할 때 객체지향적인 가치를 지킬 수 있도록 도와준다.
  • AOP는 결국 애플리케이션을 다양한 측면에서 독립적으로 모델링하고, 설계하고, 개발할 수 있도록 만들어주는 것이다. 그래서 애플리케이션을 다양한 관점에서 바라볼 수 있게 도와준다.

Aspect?

  • 그 자체로 애플리케이션의 핵심기능을 담고 있지는 않지만, 애플리케이션을 구성하는 중요한 한 가지 요소이고, 핵심기능에 부가되어 의미를 갖는 특별한 모듈을 가리킨다
  • 애스펙트는 부가될 기능을 정의한 코드인 Adivice와 Advice를 어디에 적용할 건지 결정하는 Pointcut을 함께 가지고 있다.
@Component
@Aspect //Aspect 모듈임을 선언
public class LogAspect {

    Logger logger = LoggerFactory.getLogger(LogAspect.class);

    @Around("@annotation(LogExecutionTime)") // Advice와 Pointcut
    public Object logExcutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        Object proceed = joinPoint.proceed();

        stopWatch.stop();
        logger.info(stopWatch.prettyPrint());

        return proceed;
    }

}
@Controller
public class MemberController {

  //...(생략)

    @PostMapping("/members/new")
    @LogExecutionTime //Target
    public String create(MemberForm form) { 
        Member member = new Member();
        member.setName(form.getName());

        memberService.join(member);

        return "redirect:/";
    }
}

Target

  • 부가기능을 부여할 대상
  • 핵심기능을 담은 클래스일 수도 있지만 경우에 따라서는 다른 부가기능을 제공하는 프록시 오브젝트일 수도 있다.

Advice

  • Target에게 제공할 부가기능을 담은 모듈
  • Advice는 오브젝트로 정의하기도 하지만 메소드 레벨에서 정의할 수도 있다.
  • 여러가지 종류가 있는데 MethodInterceptor처럼 메소드 호출 과정에 전반적으로 참여하는 것도 있지만, 예외가 발생했을 때만 동작하는 어드바이스처럼 메소드 호출 과정의 일부에서만 동작하는 어드바이스도 있다.

Pointcut

  • Adivice를 적용할 Join point를 선별하는 작업 또는 그 기능을 정의한 모듈을 말한다. (어디에 적용해야 하는지)
  • 스프링 AOP의 Join point는 메소드의 실행이므로 스프링의 Pointcut은 메소드를 선정하는 기능을 갖고 있다.
    • 그래서 포인트컷 표현식은 메소드의 실행이라는 의미인 execution으로 시작하고, 메소드의 시그니처를 비교하는 방법을 주로 이용한다.
    • 메소드는 결국 클래스 안에 존재하는 것이기 때문에 메소드 선정이란 결국 클래스를 선정하고 그 안의 메소드를 선정하는 과정을 거치게 된다.
    • 주요 표현식
      • execution
      • @annotation
      • bean

Join point

  • Advice가 적용될 수 있는 위치
  • 스프링의 프록시 AOP에서 Join point는 메소드의 실행단계뿐이다. Target 오브젝트가 구현한 인터페이스의 모든 메소드는 Join point가 된다.

2. AOP 적용 기술

프록시를 이용한 AOP : 스프링 AOP

프록시로 만들어서 DI로 연결된 빈 사이에 적용해 Target의 메소드 호출 과정에 참여해서 부가기능을 제공한다. 프록시 방식을 사용했기 때문에 메소드 호출 과정에서 참여해 부가기능을 제공해준다.

독립적으로 개발한 부가기능 모듈을 다양한 Target 객체의 메소드에 다이내믹하게 적용해주기 위해 가장 중요한 역할을 맡고 있는게 바로 프록시다.

바이트코드 생성과 조작을 통한 AOP : AspectJ

AOP 기술의 원조. AOP 프레임워크인 AspectJ가 있다. AspectJ는 스프링처럼 다이내믹 프록시 방식을 사용하지 않는다. 프록시처럼 간접적인 방법이 아니라, Target 객체를 뜯어고쳐서 부가기능을 직접 넣어주는 직접적인 방법을 사용한다 소스코드를 수정하는게 아니라 컴파일된 Target의 클래스 파일 자체를 수정하거나 클래스가 JVM에 로딩되는 시점을 가로채서 바이트코드를 조작한다. 결과적으로는 핵심기능과 부가기능이 같이 있게 되는 거지만 소스코드를 수정하는 것은 아니므로 개발자는 계속해서 비즈니스 로직에 충실할 수 있다.

  • 장점
    • DI 컨테이너의 도움을 받아 자동 프록시 생성 방식을 사용하지 않아도 AOP를 적용할 수 있다.
    • 컨테이너가 사용되지 않는 환경에서도 손쉽게 AOP 적용이 가능하다.
    • 프록시 방식보다 강력하고 유연한 AOP가 가능하다. 객체의 생성, 필드 값의 조회와 조작, 스태틱 초기화 등 다양한 작업에 부가기능을 부여해줄 수 있다.

3. AOP가 적용되는 예

  • 로깅
  • 트랜잭션 처리코드
  • 성능 측정
  • 인증, 권한 확인

참고자료

  • 토비의 스프링
  • 스프링 프레임워크 핵심 기술

'Spring' 카테고리의 다른 글

JDK 다이나믹 프록시와 CGLIB 프록시  (0) 2022.05.19
[Spring] IoC(Inversion of Control) 정리  (0) 2020.10.03
댓글