[Spring] AOP - AOP 개념과 Spring AOP 적용

김희정·2023년 2월 27일
1

Spring

목록 보기
5/18
post-thumbnail

💎 들어가며

이번 포스팅에서는 AOP의 개념과 Spring AOP를 적용하는 법 등 AOPSpring AOP에 대해 포스팅 해보고자 합니다.



1. AOP

1.1 AOP란?

AOP (Aspect-Oriented Programming, 관점 지향 프로그래밍) 방식은핵심 관심사(Core Concerns)와 횡단 관심사(Cross-cutting Concerns)을 분리하여 가독성과 모듈성을 증진시킬 수 있는 프로그래밍 패러다임입니다.


1.2 AOP 사용 목적

프로그램을 개발하다보면 꼭 필요한 구문이지만 반복적이면서 길이만 차지하는 코드들이 많이 발견되는 것을 볼 수 있습니다.

이런 것을 보일러 플레이트(boilerplate) 코드라고 하는데요!


가장 대표적인 예로 JDBC를 들수 있습니다.

JDBC에서는

  • Connection 객체 생성
  • Statement 객체 생성
  • SQL문 실행
  • ResultSet결과 처리
  • 객체 정리

위의 프로세스대로 최소 10줄 이상의 코드가 발생합니다. 여기서 각 로직별 꼭 필요한 부분은 SQL문을 생성하고 ResultSet 결과를 처리하는 부분만이죠.

이러한 코드가 모든 로직에 있다고 생각하면 어떨까요? 생각만 해도 읽기 어려운 느낌이 나고 수정하기 엄청 귀찮고 복잡하겠죠?


이러한 부가적인 코드들을 횡단 관심사로 분리(AOP로 처리)하고 해당 메소드에는 핵심 로직만 수행할 수 있도록 코드를 분리하는 것을 AOP라고 할 수 있습니다.

AOP는 주로 사용되는 부가 기능인 로깅, 트랜잭션, 보안, 캐싱 등에 활용됩니다.


1.2 AOP의 주요 개념

🕐 JoinPoint

특정 대상(메소드)에서 횡단 로직(Advice)이 수행될 시점을 의미합니다.

이러한 시점(JoinPoint)에는 before, after-returning, after-throwing, after, around 등이 있습니다.


👀 Pointcut

횡단 로직(Advice)이 수행될 대상을 의미합니다.

Pointcut표현식으로 표현되는데, 대표적으로는 execution이 있습니다.
excution({접근 제어자} /{패키지 이름}.{클래스 이름}.{메소드 이름({매개변수})})


💬 Advice

횡단 관심사를 구현한 실제 구현체(Class)를 의미합니다.


💌 Aspect

AspectJoinPointAdvice의 결합한 객체를 의미합니다. 다시말해서, 횡단 관심사 로직과 로직이 수행될 시점을 결합하여 모듈화한 객체입니다.


🔔 Weaving (apply)

  • Advice를 주기능에 적용하는 행위을 의미합니다.


2. Spring AOP 적용하기

2.1 AOP 라이브러리 추가

pom.xml은 Maven 프로젝트의 프로젝트 관리 파일입니다.

이 파일에 Spring AOP 라이브러리 의존성을 추가하면 자동으로 Maven이 라이브러리를 관리해줍니다.


Spring에서 aop를 사용하기 위해서는 aop 관련 라이브러리인 AspectJ를 추가해야 합니다.

Spring Legacy Project에서 Project 생성시 자동 생성되는 Runtime(rt)라이브러리가 있지만, 실제 개발을 위해서는 weaver 라이브러리를 추가해주어야 합니다.


아래와 같이 라이브러리를 추가합니다.

※ 버전은 얼마든지 변경될수 있습니다.

<org.aspectj-version>1.9.9.1</org.aspectj-version>

<!-- AspectJ Runtime -->
<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjrt</artifactId>
	<version>${org.aspectj-version}</version>
</dependency>

<!-- AspectJ Util -->
<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjweaver</artifactId>
	<version>${org.aspectj-version}</version>
</dependency>

2.2 AOP를 이용한 로직 구현

AOP를 구현하는 방법에는 XML 방식, Annotation 기반 방식으로 총 두가지가 있습니다.


🌟 XML 방식

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

    <aop:config>
     
        <!-- Spring AOP Pointcut definitions -->
        <aop:pointcut id="loggingOperation"
            expression="execution(* com.demo.aop.EmployeeManager.*(..))" />
             
        <aop:pointcut id="transactionOperation"
            expression="execution(* com.demo.aop.EmployeeManager.getEmployeeById(..))" />
 
        <!-- Spring AOP aspect 1 -->
        <aop:aspect id="loggingAspect" ref="loggingAspectBean">
             
            <!-- Spring AOP advises -->
            <aop:before pointcut-ref="loggingOperation" method="logBefore" />
            <aop:after pointcut-ref="loggingOperation" method="logAfter" />
             
        </aop:aspect>
 
        <!-- Spring AOP aspect 2 -->
        <aop:aspect id="transactionAspect" ref="transactionAspectBean">
            <aop:before pointcut-ref="transactionOperation" method="getEmployeeById" />
        </aop:aspect>
 
    </aop:config>
 
    <!-- Spring AOP aspect instances -->
    <bean id="loggingAspectBean" class="com.demo.aop.LoggingAspect" />
    <bean id="transactionAspectBean" class="com.demo.aop.TransactionAspect" />
     
    <!-- Target Object -->
    <bean id="employeeManager" class="com.demo.aop.EmployeeManagerImpl" />
 
</beans>

  1. Aspect 클래스를 구현하고 Spring Bean 등록합니다.
  2. AOP 태그로 정의합니다
    • <aop:config>(aop 루트 노드) 안에
    • <aop:pointcut> 태그로 실행시점(pointcut) 정의
    • <aop:aspect> 태그로 클래스(advice)를 실행시점(pointcut)과 결합 정의

🌟 Annotation 방식

1) Autoproxy 설정

Annotation을 적용하기 위해서는 root-context안에 aop 네임스페이스를 추가하고, autoproxy를 설정해야 합니다.

autoproxy란 @Aspect가 적용된 빈을 Aspect로 사용할 수 있게하는 설정입니다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">
	
	<!-- Root Context: defines shared resources visible to all other web components -->
	
	<!-- AspectJ 라이브러리를 이용한 Proxy 객체를 생성 -->
	<aop:aspectj-autoproxy />
</beans>

2) Annotation 종류

AnnotationDescription
@Aspect이 Class가 횡단 관심사 로직을 모듈화한 객체(Aspect)라는 것을 알려주는 Annotation
@Pointcut횡단 관심사가 적용될 JoinPoint들을 표현식으로 정의한 것
@BeforeMethod 가 호출되기 전에 실행되는 Advice 기능 수행
@AroundMethod 실행 중에 호출 전후에 어드바이스 기능을 수행
@AfterMethod의 결과에 관계 없이(성공, 예외 관련없이. 마치 finally 같은 개념) 타겟 메소드가 완료되면 어드바이스 기능을 수행
@AfterReturningMethod가 성공적으로 결과 값을 반환한 후 Advice 기능 수행
@AfterThrowingMethod가 수행 중 예외를 던지게 되면 Advice 기능 수행

3) 코드에서 사용

아래는 코드 사용 예시입니다.

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

/*
- Aspect: 해당 클래스가 Aspect라는 것을 선언합니다.
- Component: Spring이 해당 빈을 서칭할 수 있도록 선언합니다.
*/
@Aspect
@Component
public class CommonAspect {

	// before advice
	@Before("execution(* method1())")
	public void beforeMethod() {
		System.out.println("beforeMethod 호출");
	}

	// after advice
	@After("execution(* method1())")
	public void afterMethod() {
		System.out.println("afterMethod 호출");
	}

	// around advice
	@Around("execution(* method1())")
	public Object aroundMethod(ProceedingJoinPoint pjp) throws Throwable{
		System.out.println("aroundMethod 호출 1");
		Object result = pjp.proceed();
		System.out.println("aroundMethod 호출 2");
		return result;
	}

	// after-returning advice
	@AfterReturning("execution(* method1())")
	public void afterReturningMethod() {
		System.out.println("afterReturning 호출");
	}

	// after-throwing advice
	@AfterThrowing("execution(* method1())")
	public void afterThrowingMethod() {
		System.out.println("afterThrowing 호출");
	}
}


💎 Reference

profile
Java, Spring 기반 풀스택 개발자의 개발 블로그입니다.

0개의 댓글