Spring AOP는 비즈니스 로직과 부가적인 기능(로깅, 보안 등)을 분리하여 모듈화하는 방식으로, 코드의 가독성과 유지보수성을 높일 수 있습니다. 이번에는 Spring AOP의 개념과 사용법, 예제에 대해 자세히 살펴보겠습니다.
Spring AOP는 Aspect Oriented Programming(관점 지향 프로그래밍)의 약자로, 객체 지향 프로그래밍(OOP)을 보완하기 위한 프로그래밍 방식입니다. OOP는 클래스와 객체를 중심으로 프로그래밍을 하지만, AOP는 비즈니스 로직 외에 추가적인 기능(로깅, 보안 등)을 분리하여 모듈화하고, 이를 객체 지향적인 방식으로 구현하는 것입니다.
Spring AOP는 자바의 프록시 패턴을 이용하여 구현되며, Proxy 객체를 생성하여 비즈니스 로직과 부가 기능을 분리합니다. 클라이언트는 Target 객체를 호출하는 것처럼 Proxy 객체를 호출하면, Proxy 객체에서 부가 기능을 추가한 후 Target 객체의 메서드를 호출합니다.
Spring AOP에서 사용되는 요소들은 다음과 같습니다.
Spring AOP에서는 위 요소들을 이용하여 부가 기능을 Target Object에 적용합니다. Aspect는 Pointcut과 Advice를 모두 포함하며, Target Object를 대신하여 Proxy Object를 생성하여 부가 기능을 추가합니다. 이를 위해 Weaving 과정을 수행합니다.
💡 Spring AOP에서 프록시 방식을 사용하는 이유는, 비즈니스 로직과 부가 기능을 분리하여 코드를 유지보수하기 쉽게 하기 위해서입니다. 프록시 객체는 Target 객체를 대신하여 호출되어, 부가 기능을 추가한 후 Target 객체의 메소드를 호출합니다.
- 만약 프록시 객체 없이 Target 객체를 직접 참조한다면, Aspect 클래스에 정의된 부가 기능을 사용하기 위해 여러 곳에서 반복적으로 Aspect 클래스를 호출해야 하고, 이로 인해 코드의 복잡도와 유지보수성이 떨어집니다. 이를 방지하기 위해, Spring에서는 Target 객체를 상속하는 프록시 객체를 생성하여 부가 기능을 수행합니다.
- 프록시 객체는 Target 객체와 동일한 인터페이스를 구현하므로, 클라이언트는 Target 객체인지 프록시 객체인지 구분하지 않고 사용할 수 있습니다. 이렇게 함으로써, 프록시 객체는 Target 객체에 부가 기능을 추가하면서도 Target 객체의 순수한 비즈니스 로직에 집중할 수 있도록 만들어줍니다. 따라서, 코드의 복잡도를 낮추고 유지보수성을 높일 수 있습니다.
Spring AOP를 예제로 살펴보겠습니다. 예제에서는 메서드 실행 전, 후에 로그 출력, 메서드 실행 시간을 측정하는 부가 기능을 실습하도록 하겠습니다.
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<aop:aspectj-autoproxy/>
</beans:beans>
<!--
AspectJ Weaver
AOP를 사용하기 위해서 추가함.
PointCut 어노테이션, pom.xml에 버전 이슈(1.9.0) 버전 사용 필요
-->
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.0</version>
</dependency>
package com.kh.spring.common;
public class LogAdvice {
public void printLog() {
System.out.println("[공통로그 - LogAdvice] 비즈니스 로직 수행 전 동작");
}
}
package com.kh.spring.common;
import org.aspectj.lang.ProceedingJoinPoint;
public class Log4jAdvice {
public void printLogging() {
System.out.println("[공통로그 - Log4j] 비즈니스 로직 수행 후 동작하긔!!");
}
public Object aroundLog(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("[BEFORE] : 비즈니스 메소드 수행 전에 처리할 내용");
Object returnObj = pjp.proceed(); // 실행되는 메소드의 시점을 정하여 리턴해줌
System.out.println("[AFTER] : 비즈니스 메소드 수행 후에 처리할 내용");
return returnObj;
}
}
<!-- AOP XML 방식 -->
<bean id="log" class="com.kh.spring.common.LogAdvice"></bean>
<bean id="logger" class="com.kh.spring.common.Log4jAdvice"></bean>
<aop:config>
<aop:pointcut expression="execution(* com.kh.spring..*Impl.*(..))" id="allPointCut"/>
<aop:aspect ref="logger">
<aop:before method="printLog" pointcut-ref="allPointCut"/>
<aop:after method="printLogging" pointcut-ref="allPointCut"/>
<aop:around method="aroundLog" pointcut-ref="allPointCut"/>
</aop:aspect>
</aop:config>
Spring AOP는 비즈니스 로직과 부가 기능을 분리하여 모듈화하고, 객체 지향적인 방식으로 구현하는 방식입니다. Aspect, Pointcut, Advice, Target Object, Proxy Object 등의 요소를 정의하여 사용하며, XML 설정 파일이나 Annotation을 이용하여 구현할 수 있습니다. Spring AOP를 이용하면 코드의 가독성과 유지보수성을 높일 수 있으며, 로깅, 보안 등의 부가 기능을 구현하기에 적합합니다.