본문 바로가기

CS study/spring

AOP의 개념과 적용하기

목차

     

    개요

    https://csg1353.tistory.com/90

     

     

    위의 포스팅처럼 elasticsearch의 logstash 로그를 수집하기 위해 AOP의 개념을 사용해 로그를 수집해보려 한다.

    이를 위해서는 먼저 AOP에 대한 개념을 학습해야 할 것이다.

     

    AOP란 (Aspect-Oriented Programming)

    정의

    공통 기능(예: 로깅, 보안)을 핵심 비즈니스 로직에서 분리하고, 이러한 공통 기능을 필요한 위치에 적용
    어떤 로직을 기준으로 핵심적인 관점, 부가적인 관점으로 나누어서 보고 그 관점을 기준으로 각각 모듈화 한 것.

     

    AOP는 기존 OOP의 객체 지향적인 프로그래밍이 아닌, Aspect(관점)을 기준으로 프로그래밍하는 기법이다.

    공통 기능의 관심 사항과 핵심 관심 사항 등, 객체지향으로는 분리할 수 있는 전체의 '관점' 을 기점으로 삼아 코드의 간결성을 높이는 기법이다.

     

    로깅, 트랜잭션, 시큐리티 등.. 모든 객체에 쓰이는 공통된 로직이 존재한다.

     

    우리는 OOP에서 객체와 클래스, 비즈니스 로직 단위로 시스템과 코드를 구분하지만, 위의 예시와 같이 전체적으로 모든 로직에서 동작하는 '공통 로직'이 존재한다.

    이러한 개념들을 공통 관심 사항 (cross-cutting concern) 과 핵심 관심 사항 (core concern) 이라고 한다.

     

    - 핵심 관점은 결국 우리가 적용하고자 하는 핵심 비즈니스 로직이다.

    - 공통 관심 사항은 핵심 관점을 실행하기 위해 행해지는 로깅, 트랜잭션, 시큐리티 등을 의미한다.

     

     

    그래서 결론은?

    객체 단위로 보자면, 모든 클래스에서 공통적으로 실행되는 로직이 존재한다.

     

    소스 코드상에서 여러 클래스나 비즈니스 로직마다 계속해서 반복해서 쓰이는 공통 코드들을 발견할 수 있는데,

    이를 흩어진 관심사(Crosscutting Concerns) 라 부른다.

     

    이처럼 흩어진 관심사를 Aspect의 개념으로 모듈화하고, 핵심적인 비즈니스 로직에서 분리해서 재사용하는 것이 AOP의 목적이다.

     

    시간 측정 로직을 AOP 적용한 예시. 각 객체의 공통적인 로직을 분리하였다.

     

     

    Spring AOP

    spring에서의 AOP 적용 방식

    AOP를 적용하는 방식은 여러 가지가 있다

    1. 컴파일 시점 적용

    - .java 파일 컴파일 시 AOP를 적용하고 .class 바이트코드 파일로 컴파일 (Aspect와 실제 코드를 연결하는 것을 weaving이라고 한다.)

    - 이 경우 더 넓은 범위의 부가 기능 적용이 가능하다.

     

    2. 클래스 로딩 시점 적용

    - JVM의 ClassLoader에 바이트코드를 올리는 시점에, 바이트코드를 조작해 부가기능을 추가하는 방식

     

    3. 런타임 시점 적용

    - 메인 메서드 실행 이후 자바가 제공하는 범위 내에 부가 기능을 적용하는 방식이다.

     

    Spring에서의 AOP는 런타임 시점에  AOP를 적용하는 방식을 사용한다.

    컴파일 시점이나 클래스 로딩 시점에 적용하는 다른 AOP 방식과 달리, Spring AOP는 런타임에 동적으로 프록시를 생성하여 부가 기능을 적용한다.

     

    - 프록시 기반의 접근: 프록시 패턴을 사용하여 부가 기능을 적용한다. 프록시는 실제 객체를 감싸서 클라이언트의 요청을 처리하고, 필요에 따라 실제 객체에 요청을 전달한다.

    - 메서드 실행 시점의 제한: 부가 기능의 적용은 메서드 호출 시점에 제한된다. 프록시는 메서드가 호출될 때 해당 기능을 실행하고, 필요한 경우 실제 메서드 호출을 진행한다.

     

     

    왜 런타임에 AOP를 적용하는가?

     

    1. 동적 프록시

    런타임 시 프록시를 사용하면, 객체의 행동을 프로그램 실행 중에 유연하게 변경할 수 있다.

    이는 특정 조건이나 설정에 따라 동적으로 AOP의 적용 여부를 결정할 수 있게 해준다.

     

    2. 스프링 생태계와의 호환

    Spring AOP는 Spring 프레임워크와 긴밀하게 통합되어 있다.

    프록시를 사용하면, Spring의 Dependency Injection, Bean lifecycle 등과 같은 기능들과 원활하게 연동할 수 있다.

     

    3. 상대적으로 가벼움

    컴파일 시점의 AOP 구현(예: AspectJ)은 더 강력하고 유연하지만, 성능 오버헤드와 구현 복잡성이 증가한다.

    Spring AOP는 이런 복잡성을 줄이면서 상당수 부분에 필요한 기능을 제공한다.

     

    AOP 용어와 개념

    AOP를 한 눈에 알아보는 도식도

    Aspect (에스팩트)

    에스팩트는 공통적인 기능이나 행동을 정의하는데 사용된다.

    로깅이나 보안 검사 등 여러 서비스나 컴포넌트에 걸쳐 반복되는 기능을 에스팩트를 통해 중앙에서 관리할 수 있다.

    • 정의: 애플리케이션의 횡단 관심사(Cross-Cutting Concern)를 모듈화한 것.
    • 구성: 어드바이스(Advice)와 포인트컷(Pointcut)의 조합으로, 공통 기능을 나타냄.
    • 예시: 로깅, 보안 체크, 트랜잭션 처리 등.

    Join Point (조인 포인트)

    조인 포인트는 AOP가 적용될 수 있는 실제 지점이다.

    메서드 호출, 메서드 종료, 객체 생성 등이 조인 포인트가 될 수 있다.

    • 정의: 애플리케이션 실행 흐름에서 AOP를 적용할 수 있는 특정 지점.
    • 스프링에서의 한계: 스프링 AOP에서는 주로 메서드 실행 지점에 한정된다.
    • 예시: 클래스 초기화, 메서드 호출, 예외 발생.

    Advice (어드바이스)

    어드바이스는 에스팩트의 실제 행동을 정의한다.

    예를 들어, Before 어드바이스는 메서드 실행 전에 로그를 기록하거나, After returning은 메서드가 성공적으로 반환된 후에 특정 작업을 수행할 때 사용된다.

    • 정의: 조인포인트에서 실행되는 실질적인 부가 기능 코드.
    • 주요 타입:
      • Before: 조인포인트 실행 이전에 실행.
      • After returning: 조인포인트가 예외 없이 완료된 후 실행.
      • After Throwing: 메서드가 예외를 던지는 경우 실행.
      • After (finally): 조인포인트의 성공/실패 여부에 관계없이 실행.
      • Around: 조인포인트 전후에 실행, 가장 유연하고 강력한 어드바이스.

    Pointcut (포인트컷)

    포인트컷은 어떤 조인 포인트(특정 메서드나 메서드 패턴)에 어드바이스를 적용할지 결정한다.

    이를 통해 특정 클래스나 메서드에만 AOP를 적용할 수 있다.

    • 정의: 어드바이스가 적용될 조인 포인트를 선별하는 표현식.
    • 사용 방법: 주로 AspectJ의 표현식을 사용하여 지정.
    • 기능: 어떤 조인 포인트에 어드바이스를 적용할지 결정.

    Target (타겟)

    AOP를 적용할 실제 객체이다. 포인트컷에 의해 선택되는 이 객체들에 어드바이스가 적용된다.

    • 정의: 핵심 기능을 담은 모듈, 어드바이스가 적용될 대상.
    • 선정 기준: 포인트컷에 의해 결정되는 객체.

    Advisor (어드바이저)

    특정 어드바이스와 포인트컷의 조합.

    이를 통해 어떤 어드바이스를 어떤 포인트컷에 적용할지 결정한다.

    • 정의: 스프링 AOP에서 사용되는 용어로, 하나의 어드바이스와 하나의 포인트컷으로 구성된 에스팩트.
    • 역할: 어드바이스와 포인트컷의 조합을 관리.

     

    실제 적용해보기

     

    Elasticsearch 로그스태시 수집을 위해, 본체 서버에서 모든 메서드 실행 동작 이후(성공적으로 실행했다면) logback을 통해 로그를 저장하는 기능을 만들고자 한다.

    단계적으로 이를 적용해보도록 하자.

     

    의존성 추가

    dependencies {
        // Spring AOP 의존성
        implementation 'org.springframework.boot:spring-boot-starter-aop'
    
        // Logback 의존성
        implementation 'ch.qos.logback:logback-classic'
    }

     

    Aspect 클래스 생성

    package com.sch.sch_elasticsearch.aop;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Aspect;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    
    
    @Aspect
    @Component
    public class LoggingAspect {
        private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
    
        //지정된 포인트컷 표현식에 맞는 메서드가 성공적으로 반환될 때 실행
        @AfterReturning("execution(* com.sch.sch_elasticsearch.domain..*.*(..))")
        public void logAfterMethodReturn(JoinPoint joinPoint) {
            logger.info("메서드 실행 완료: " + joinPoint.getSignature().toShortString());
        }
        
    }

     

    실행 결과

     

    테스팅 코드를 실행하자, 로거가 찍히는 모습을 확인할 수 있다.

     

     

    출처

    < AOP 개념 >

     

    https://velog.io/@kai6666/Spring-Spring-AOP-%EA%B0%9C%EB%85%90

    https://engkimbs.tistory.com/entry/%EC%8A%A4%ED%94%84%EB%A7%81AOP

    https://engkimbs.tistory.com/entry/%EC%8A%A4%ED%94%84%EB%A7%81AOP

     

     

     

    .