Springboot

[Spring] Spring AOP(Aspect-Oriented Programming)

KJihun 2025. 4. 16. 10:59
728x90

핵심 기능(주관심사)과 공통 기능(보조 관심사, 부가기능)을 분리하여 코드의 재사용성, 유지보수성, 가독성을 향상시키는 방법

 

주 관심사 : 실제 로직 (ADD 함수 등)

보조 관심사 : 보안, 로그, 트랜잭션 등

 

중간에 'Proxy' 객체를 두어, 보조관심사를 자동으로 끼워 넣는다.

 

 

AOP 동작 흐름 (Spring AOP 기준)

  1. 클라이언트가 메서드 호출 (예: ADD(x, y))
  2. Proxy 객체가 대신 호출을 가로챔
  3. 보조관심사 로직 실행 (예: 로그 찍기, 트랜잭션 시작 등)
  4. 실제 핵심 로직(ADD()) 실행
  5. 결과를 기반으로 보조관심사 마무리 작업 (예: 트랜잭션 커밋, 로그 종료 등)
  6. 클라이언트에게 결과 반환

 

주 사용 목적

로깅 (Logging) 메서드 실행 시점, 매개변수, 리턴 값 등을 기록
보안 (Security) 인증, 인가 처리
트랜잭션 (Transaction) DB 작업의 일괄 처리, rollback 관리
캐싱 (Caching) 자주 호출되는 메서드 결과 캐싱
예외 처리 (Exception Handling) 예외 발생 시 별도 처리 (예: 알림, 로깅 등)

 

Spring AOP 구성요소

  • 어드바이스 (Advice): 언제 실행할지 결정
  • 포인트컷 (Pointcut): 어디에 적용할지 결정

 

AOP 용어

JoinPoint 어드바이스가 적용될 수 있는 프로그램 실행 지점
예: 메서드 호출 시점, 예외 발생 시점 등
PointCut  어떤 JoinPoint에 어드바이스를 적용할지 선택하는 필터 기준
예: 특정 패키지의 모든 get*() 메서드
Advice  실제로 끼워 넣는 코드 (보조 관심사 로직)
Aspect PointCut + Advice = 횡단 관심사 모듈
Weaving 어드바이스를 실제 코드 실행 흐름에 적용하는 과정
Proxy 클라이언트와 실제 객체 사이에 위치, 어드바이스를 삽입하는 중간 대리자 역할

 

어드바이스 종류


@Before 핵심 기능 실행 전 유효성 검사, 로깅
@After 핵심 기능 실행 후 (성공/실패 무관) 리소스 해제, 공통 메시지 출력
@AfterReturning 핵심 기능이 정상 실행 후 리턴값 로깅
@AfterThrowing 예외 발생 시 예외 처리, 알림 전송
@Around 실행 전/후/예외 모두 처리 가장 강력한 방식 – proceed()로 실제 메서드 호출

 

Spring AOP 특징

기반 프록시 기반 (JDK 또는 CGLIB)
위빙 시점 런타임 (Runtime)
JoinPoint 범위 메서드 단위만 가능 (메서드 실행 지점만)
구현 방식 표준 자바 클래스 (POJO)를 기반으로 구성 가능

 

AOP 예시코드

로그를 출력하는 AOP (Spring Framework 5 기준, XML 설정)

public class LogAspect {
    public void beforeLog(JoinPoint joinPoint) {
        System.out.println(">>> 메서드 실행 전: " + joinPoint.getSignature().getName());
    }

    public void afterLog(JoinPoint joinPoint) {
        System.out.println(">>> 메서드 실행 후: " + joinPoint.getSignature().getName());
    }
}
 applicationContext.xml (XML 기반 AOP 설정)

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
         http://www.springframework.org/schema/beans 
         http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/aop 
         http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 서비스 Bean -->
    <bean id="myService" class="com.example.service.MyService" />

    <!-- Aspect Bean -->
    <bean id="logAspect" class="com.example.aspect.LogAspect" />

    <!-- AOP 설정 -->
    <aop:config>
        <aop:aspect ref="logAspect">
            <aop:pointcut id="serviceMethods" expression="execution(* com.example.service.*.*(..))" />
            <aop:before method="beforeLog" pointcut-ref="serviceMethods" />
            <aop:after method="afterLog" pointcut-ref="serviceMethods" />
        </aop:aspect>
    </aop:config>
</beans>

 

 

 AOP 클래스 LogAspect.java

package com.example.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogAspect {

    @Before("execution(* com.example.service.*.*(..))")
    public void beforeLog(JoinPoint joinPoint) {
        System.out.println(">>> [Before] 메서드 실행 전: " + joinPoint.getSignature().getName());
    }

    @After("execution(* com.example.service.*.*(..))")
    public void afterLog(JoinPoint joinPoint) {
        System.out.println(">>> [After] 메서드 실행 후: " + joinPoint.getSignature().getName());
    }
}
AppConfig.java
package com.example.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan(basePackages = "com.example") // com.example 하위 패키지 스캔
@EnableAspectJAutoProxy
public class AppConfig {
}


MainApp.java
package com.example;

import com.example.service.MyService;
import com.example.config.AppConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainApp {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =
            new AnnotationConfigApplicationContext(AppConfig.class);

        MyService service = context.getBean(MyService.class);
        service.doSomething();

        context.close();
    }
}

 

 

 

 

 

 

'Springboot' 카테고리의 다른 글

[Spring] Filter, Interceptor, AOP의 차이점  (0) 2025.04.16
[Security] Spring Security의 흐름  (0) 2025.04.02
[Springboot] Client-Server 구조 정리  (0) 2025.03.26
[Springboot] JWT 0.15.2  (0) 2024.06.22
[Springboot] QueryDSL 2  (0) 2023.08.09