코딩 개발/Spring

AOP (Aspect Oriented Programming) 관점 지향 프로그래밍

호소세 2023. 7. 8. 11:56
728x90
반응형

AOP란?

Aspect => 관점   

Core Concern 핵심관심사 + Cross Cutting Concern 횡단관심사 or 공통관심사 

 

시스템을 핵심관심사항과 공통관심사항으로 구분해 분석, 설계, 구현, 운영합니다.

 

공통, 반복 작업을 피하고 효과적인 유지보수가 가능합니다.

=> 응집도를 높이고 결합도를 낮춥니다.

=> 응집도를 높임 : core 담당자는 core에 집중, cross cutting 공통관심사(ex-보안, 로깅, 트랜잭션..) 담당자는 자신의 역할에 집중합니다.

=> 결합도를 낮춤 : 공통 관심사 로직이 변경될 때 기존 코드는 수정이 필요하지 않고 해당 AOP 공통관심사 로직만 변경하면 됩니다.

 

Core Concern(핵심관심사) : 시스템 업무 목적에 해당하는 주요 로직 ( ex- 회원관리, 결제 등 ) 

Cross Cutting Concern(횡단 or 공통관심사) : 시스템 여러 부분에 걸쳐 적용되는 공통 로직 (ex- 트랜잭션, 보안, 로깅 등 )

 

정리하자면,

AOP는 일반적으로 프록시(proxy)를 사용하여 Aspect를 적용합니다. 프락시는 핵심 로직을 감싸고 있는 중간 계층으로서, 핵심 로직을 호출하기 전에 Aspect의 코드를 실행하거나, 호출 후에 추가적인 동작을 수행합니다. 이를 통해 핵심 로직에 부가적인 관심사를 적용할 수 있습니다.

 

AOP를 사용하면 핵심 로직에 집중하여 개발할 수 있으며, 공통적인 기능들을 한 곳에서 관리할 수 있습니다. 예를 들어, 여러 클래스에서 반복적으로 사용되는 로깅 기능을 Aspect로 정의하고, 해당 Aspect를 핵심 로직에 적용함으로써 중복 코드를 제거할 수 있습니다. 또한, 트랜잭션 관리, 보안, 성능 모니터링 등과 같은 부가적인 기능을 필요에 따라 쉽게 적용할 수 있습니다.

 

스프링 프레임워크는 AOP를 지원하고, @Aspect 어노테이션과 @Before, @After, @Around 등의 Advice 어노테이션을 제공하여 AOP를 적용할 수 있도록 합니다. 스프링의 AOP 기능을 사용하면 XML 설정이나 어노테이션을 통해 Aspect를 정의하고, Pointcut과 Advice를 지정하여 핵심 로직에 AOP를 적용할 수 있습니다.

 

AOP 적용

이렇게 Service 마다 중복되는 메서드가 많은데 저 메서드를 Service 마다 하나하나 쓰다가 1억 개의 Service가 생기게 되면 1억 번 똑같은 코드를 작성해야 합니다. 그래서 저렇게 중복되는 기능들을 하나에 묶어서 사용하는 것이 AOP입니다.

이렇게 만들어줍니다.

 

간단하게 중복 코드를 하나로 묶어서 처리해 준다고 생각하면 될 것 같습니다. 그 방법을 Spring에서는 더욱 쉽게 작성할 수 있는 것 같습니다.

 

AOP 예시

AOP 적용 안된 예시

처음에는 AOP가 안된 코드를 보여드릴게요.

<MemberServiceImpl.java>

public class MemberServiceImpl implements MemberService{
	@Override
	public void findMemberById() {
		System.out.println("**공통관심사항: 특정요구사항을 수행 findMemberById**");
		System.out.println(" core concern findMemberById");
	}
	@Override
	public void findMemberByAddress() {
		System.out.println("**공통관심사항: 특정요구사항을 수행 findMemberByAddress**");
		System.out.println(" core concern findMemberByAddress");
	}
	@Override
	public void findMemberList() {
		System.out.println("**공통관심사항: 특정요구사항을 수행 findMemberList**");
		System.out.println(" core concern findMemberList");
	}
	@Override
	public void register() {
		System.out.println(" core concern register");
	}
	// 그외 많은 메서드가 있다고 가정 
}

메서드가 있는데 공통 관심사가 필요한 메서드가 많아진다면 어떻게 될까요?

메서드를 추가할 때마다 저 문장을 작성해야 하고, 공통관심사항에 수정사항이라도 있다가는..... 저는 못 고칩니다.

 

그렇기에 하나로 묶어서 사용해야 한다는 것입니다. 수정사항이 있으면 하나만 고치면 되고 확장할 때도 따로 작성하지 않아도 됩니다. 그냥 메서드의 이름만 잘 맞춰서 만들어주면 됩니다.

 

Spring AOP 적용 사항

Maven pom.xml 파일에 aspectjweaver 추가해야 합니다.

Maven repository에 aspectjweaver을 검색해서 추가하셔도 되고 제가 작성한 코드를 작성해도 됩니다.

<pom.xml>

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjweaver</artifactId>
	<version>1.8.1</version>
</dependency>

설정파일 작성이 끝나면 본격 코드로 가볼까요

 

AOP 적용 예시 (진짜)

<CommonAspect.java>

@Component // bean 으로 스프링 컨테이너가 관리하게 함 
@Aspect // AOP 공통관심사 Cross Cutting Concern 로직 정의한 클래스임을 스프링컨테이너에 알린다
public class CommonAspect {
	@Before("execution(public * myproject.model.*Service.find*(..))")
	public void execute(JoinPoint point) { // JoinPoint : 실제 수행되는 core 대상 정보를 저장  
		String className=point.getTarget().getClass().getName();
		String methodName=point.getSignature().getName();
		System.out.println("**AOP 방식 공통관심사항: 특정요구사항을 수행** "+methodName);
	}
}

1. @Component를 작성하면서 Spring Container가 Bean으로 관리하게 합니다.

2. AOP로 사용하겠다고 @Aspect 어노테이션을 작성합니다.

3. @Before 어노테이션은 핵심 관심사가 실행되기 전에 실행되라는 명령어입니다. 이 어노테이션에 대해서는 몇 가지가 더 있는데 logging을 알아볼 때 더 깊이 알아보겠습니다.

4. @Before 어노테이션 안에 있는 것들을 분석해 보겠습니다.

5. execution : AOP 공통기능 적용 대상을 지정합니다.

6. public : 대상 메서드 접근 제어자입니다.

7. * : return 타입입니다. void, String, int 등을 작성하는 것이지요?

8. myproject.model. : 지정 패키지 하위를 대상 합니다.

9. *Service : Service로 마치는 인터페이스 및 클래스를 대상

10. find*(..) : find로 시작되는 메서드와 매개변수 0~N까지 모두를 대상으로 AOP 적용 대상으로 선택합니다.

 

그림으로 설명하면 조금 더 편할까 해서 그려와 봤습니다!

그림 설명

색 별로 구분했는데... 이해가 잘 되시나요. 제가 미술 쪽에는 아예 재능이 없다 보니... ㅎㅎ 감안해 주세요.

 

이렇게 설정하고 excute 메서드를 작성하고 JoinPoint 매개변수를 넣습니다.

 

JoinPoint 매개변수는 실제 수행되는 Core(핵심 관심사) 대상 정보를 저장합니다.

 

이렇게 하면 지정한 core 메서드가 실행하기 전에 무조건 execute 안에 있는 동작이 일어납니다.

 

Aspect 가 실행되려면 AppConfig 클래스에도 어노테이션 하나를 추가해야 합니다.

<AppConfig.java>

@Configuration
@ComponentScan("myproject")
@EnableAspectJAutoProxy // Annotation 기반 AOP 설정 
public class AppConfig {
}

@EnableAspectJAutoProxy를 추가해야 합니다.

 

 

그리고 MemberServiceImpl에는 중복되는 코드를 지우고 Test 문장을 실행해 보겠습니다.

<TestAOP.java>

public class TestAOP {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext ctx=new AnnotationConfigApplicationContext(AppConfig.class);
		MemberService ms=(MemberService) ctx.getBean("memberServiceImpl");
		System.out.println(ms.getClass().getName());//com.sun.proxy.$Proxy19
		ms.findMemberByAddress();
		ms.findMemberById();
		ms.findMemberList();
		ms.register();
		ctx.close();
	}
}

com.sun.proxy.$Proxy19

**AOP 방식 공통관심사항: 특정요구사항을 수행** findMemberByAddress

core concern findMemberByAddress

**AOP 방식 공통관심사항: 특정요구사항을 수행** findMemberById

core concern findMemberById

**AOP 방식 공통관심사항: 특정요구사항을 수행** findMemberList

core concern findMemberList

core concern register

이런 식으로 결과가 나옵니다.

 

memberService를 확인해 보니 Proxy로 저장되는 것 보이시나요? 추상화하여 대리인처럼 일을 해줍니다.

저희가 자주 들어봤던 프락치인 것이죠. 주식시장에서 누군가 매수 매도 주문을 하면 프락치는 그것을 듣고 매수 매도만 해주면 되는 것처럼 말이죠.

 

그리고 @Before 어노테이션을 사용했더니 역시 find 메서드들이 실행되기 전에 Aspect에 저장한 메서드가 실행됩니다.

 

소감

일단 글보다 그림 설명이 잘 되었는지가 가장 궁금합니다. 글 쓰는 것보다 그림을 만들어서 뽑아내는 것이 시간이 더 걸리기 때문입니다. 예술적 감각은 없지만 막 못 알아볼 정도는 아니라고 생각되네요.

다음 시간에는 Logging 관련 글을 작성해보려고 합니다.

반응형