AOP

기록하는 용도·2022년 11월 18일
0

AOP(Aspect Oriented Programming)

관점 지향 프로그래밍

  • 시스템을 핵심관심사(Core Concern) 와 횡단관심사(Cross-cutting
    Concern)로 구분하여 설계와 구현을 한다.
  • Aspect는 핵심관심사와 횡단관심사 두가지 관점으로 나뉜다.
  • 핵심관심사(Core Concern)란 시스템의 목적에 해당하는 주요 로직
    부분을 말한다.
  • 횡단관심사(Cross-cutting Concern)란 시스템의 여러 부분에 걸쳐
    공통적이고 반복적으로 필요로 하는 처리내용을 말한다.

AOP는 시스템의 여러 영역에 걸쳐 공통적이고 반복적으로 적용된
횡단관심사(Cross-Cutting Concern)를 분리하여 별도의 모듈에서 설계,
구현,운영하는 프로그래밍 기법이다.
대표적인 횡단관심사는 로깅, 보안, 트랙잭션 관리, 예외 처리등이 있다.
AOP는 “OOP를 더욱 OOP 답게 한다”.



AOP 적용전

MemberService의 회원core는 회원정보 수정, 회원정보 등록 등이 있을 것이다. 이런 과정은 회원core에서만 할 수 있는일이다.
마찬가지로 ProductService에서 상품core에서는 상품판매, 상품배달 등의 과정이 이루어질 것이다.

트랜잭션 관리, 로깅, 보안은 각 서비스에서 반드시 필수적으로 필요로 하는 기능이다. 공통 작업을 잘라내 별도의 모듈에서 처리할 수 있다.



AOP 적용후


공통 관심사는 별도의 모듈에서 만들어 놓고 엮어주기로 한다.
각 메소드에서 일일이 하지않고 별도의 모듈에서 한 번 만들어 명령하게되면 메소드가 100개든 상관없이 적용이 되기때문에 반복 작업을 피하고, 생산성이 높아져 유지보수성이 좋아진다.




예제

MemberServiceImpl.java

import org.springframework.stereotype.Service;

@Service
public class MemberServiceImpl implements MemberService {
	@Override
	public void findMemberById() {
		System.out.println("core concern : MemberServiceImpl findMemberById");
	}
	@Override
	public void findMemberByName() {
		System.out.println("core concern : MemberServiceImpl findMemberByName");
	}
	
	@Override
	public void register() {
		System.out.println("core concern : MemberServiceImpl register");
	}
	
	@Override
	public void findMemberByAddress() {
		System.out.println("core concern : MemberServiceImpl findMemberByAddress");
	}
	//그 외 여러 메소드가 있다고 가정한다.
}

MemberService.java

public interface MemberService {

	void findMemberById();

	void findMemberByName();

	void register();

	void findMemberByAddress();
	//그 외 여러 메소드가 있다고 가정한다.
}


ProductServiceImpl.java

import org.springframework.stereotype.Service;

@Service
public class ProductServiceImpl implements ProductService {

	@Override
	public void findProductById() {
		System.out.println("core concern : ProductServiceImpl findProductById");
	}

	@Override
	public void findProductByMaker() {
		System.out.println("core concern : ProductServiceImpl findProductByMaker");
	}

	@Override
	public void deleteProduct() {
		System.out.println("core concern : ProductServiceImpl deleteProduct");
	}

}

ProductService.java

public interface ProductService {
	void findProductById();
	void findProductByMaker();
	void deleteProduct();
}


TestAopEx.java

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestAopEx {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext ctx=new AnnotationConfigApplicationContext(AppConfig.class);
		//System.out.println(ctx.getBean("memberServiceImpl"));
		//System.out.println(ctx.getBean("productServiceImpl"));
		MemberService ms=(MemberService)ctx.getBean("memberServiceImpl");
		ProductService ps=(ProductService)ctx.getBean("productServiceImpl");
		ms.register();
		ms.findMemberById();
		ms.findMemberByName();
		ms.findMemberByAddress();
		ps.deleteProduct();
		ps.findProductById();
		ps.findProductByMaker();
		ctx.close();
	}
}

core concern : MemberServiceImpl register
core concern : MemberServiceImpl findMemberById
core concern : MemberServiceImpl findMemberByName
core concern : MemberServiceImpl findMemberByAddress
core concern : ProductServiceImpl deleteProduct
core concern : ProductServiceImpl findProductById
core concern : ProductServiceImpl findProductByMaker

실제로 core concern을 만들어 실행했다고 볼 수 있다.

요구사항

현 시스템의 기능 중 검색 기능, find로 시작되는 메소드들을 대상으로 특정 기능 (검색어 로깅, 검색어 체크 등..)을 적용해야한다.

검색 기능 관련 메소드가 현 시스템에서 25개 존재한다고 가정하자.



방안1 - 각 메소드에 직접 기능을 삽입

: 서비스 규모가 클수록 반복적 작업 증가 -> 생산성이 낮다.
이후 요구 사항이 변경된다면 다시 많은 공통 작업들이 다수 수정되어야 한다.-> 낮은 유지보수성
특정 기능에 대한 전문화도 떨어진다.

MemberServiceImpl.java

import org.springframework.stereotype.Service;

@Service
public class MemberServiceImpl implements MemberService {
	@Override
	public void findMemberById() {
		System.out.println("cross cutting concern : 검색 계열 메소드에 공통 작업");
		System.out.println("core concern : MemberServiceImpl findMemberById");
	}
	@Override
	public void findMemberByName() {
		System.out.println("cross cutting concern : 검색 계열 메소드에 공통 작업");
		System.out.println("core concern : MemberServiceImpl findMemberByName");
	}
	
	@Override
	public void register() {
		System.out.println("core concern : MemberServiceImpl register");
	}
	
	@Override
	public void findMemberByAddress() {
		System.out.println("cross cutting concern : 검색 계열 메소드에 공통 작업");
		System.out.println("core concern : MemberServiceImpl findMemberByAddress");
	}
	//그 외 여러 메소드가 있다고 가정한다.
}

ProductServiceImpl.java

import org.springframework.stereotype.Service;

@Service
public class ProductServiceImpl implements ProductService {

	@Override
	public void findProductById() {
		System.out.println("cross cutting concern : 검색 계열 메소드에 공통 작업");
		System.out.println("core concern : ProductServiceImpl findProductById");
	}

	@Override
	public void findProductByMaker() {
		System.out.println("cross cutting concern : 검색 계열 메소드에 공통 작업");
		System.out.println("core concern : ProductServiceImpl findProductByMaker");
	}

	@Override
	public void deleteProduct() {
		System.out.println("core concern : ProductServiceImpl deleteProduct");
	}
	//그 외 많은 메소드가 있다고 가정

}

TestAopEx.java

public class TestAopEx {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext ctx=new AnnotationConfigApplicationContext(AppConfig.class);
		//System.out.println(ctx.getBean("memberServiceImpl"));
		//System.out.println(ctx.getBean("productServiceImpl"));
		MemberService ms=(MemberService)ctx.getBean("memberServiceImpl");
		ProductService ps=(ProductService)ctx.getBean("productServiceImpl");
		ms.register();
		ms.findMemberById();
		ms.findMemberByName();
		ms.findMemberByAddress();
		ps.deleteProduct();
		ps.findProductById();
		ps.findProductByMaker();
		ctx.close();
	}
}

core concern : MemberServiceImpl register
cross cutting concern : 검색 계열 메소드에 공통 작업
core concern : MemberServiceImpl findMemberById
cross cutting concern : 검색 계열 메소드에 공통 작업
core concern : MemberServiceImpl findMemberByName
cross cutting concern : 검색 계열 메소드에 공통 작업
core concern : MemberServiceImpl findMemberByAddress
core concern : ProductServiceImpl deleteProduct
cross cutting concern : 검색 계열 메소드에 공통 작업
core concern : ProductServiceImpl findProductById
cross cutting concern : 검색 계열 메소드에 공통 작업
core concern : ProductServiceImpl findProductByMaker

현재는 출력문을 복사 붙여넣기 해 corss cutting concern을 추가했지만, 실제로는 데이터베이스에 검색어를 insert 시키는등의 작업이 필요할 것이다.



방안2 - AOP 적용하는 방안

코드에 손대지않고 별도의 모듈에서 처리해보기
먼저 AppConfig에서 추가 설정이 필요하다.
Annotation 기반 AOP 지원 설정인 @EnableAspectJAutoProxy 어노테이션이 필요하다.

1. maven의 pom.xml : aop 관련 라이브러리 추가

2. AOP 모듈 클래스 정의 :

cross cutting concern logic 구현
before advice : 공통 로직을 타겟 메소드 실행전에 적용

CommonAspect 클래스를 새로 만든다.

CommonAspect.java

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect // AOP 담당 객체 (Cross Cutting Concern 로직을 정의한 객체임을 스프링 컨테이너에게 알리는 어노테이션)
@Component
public class CommonAspect {
	@Before("execution(public * org.kosta.myproject.model.*Service.find*(..))") //
	public void execute() {
		System.out.println("**AOP** cross cutting concern : 검색 계열 메소드에 공통 작업");
	}
}

public void execute() {
		System.out.println("**AOP** cross cutting concern : 검색 계열 메소드에 공통 작업");
	}

공통 작업을 메소드에 넣어준다.

3. AOP 설정 : AppConfig에 AOP 설정 추가

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

@Configuration
@ComponentScan("org.kosta.myproject")
@EnableAspectJAutoProxy // Annotation 기반 AOP 지원 설정
public class AppConfig {

}

AOP cross cutting concern : 검색 계열 메소드에 공통 작업
core concern : MemberServiceImpl findMemberByAddress
AOP cross cutting concern : 검색 계열 메소드에 공통 작업
core concern : MemberServiceImpl findMemberById
core concern : MemberServiceImpl register
AOP cross cutting concern : 검색 계열 메소드에 공통 작업
core concern : MemberServiceImpl findMemberByName
core concern : ProductServiceImpl deleteProduct
AOP cross cutting concern : 검색 계열 메소드에 공통 작업
core concern : ProductServiceImpl findProductById
AOP cross cutting concern : 검색 계열 메소드에 공통 작업
core concern : ProductServiceImpl findProductByMaker

before advice : 타겟 메소드 실행 전에 cross cutting (공통 로직) 을 적용
pointcut : AOP 적용 대상을 지정
public : 메소드 접근제어자
* : 메소드 리턴타입 void 포함 모든것
org.kosta.myproject.model : 해당 패키지 이하
* : Service service로 끝나는 인터페이스 or 클래스
find : find로 시작되는 메소드
(..) : 매개변수가 0~*

cross cutting concern (공통 로직)의 업데이트가 필요하고 적용시점 또한 대상 메소드가 실행 전이 아니라 실행 후 시점에 적용되어야한다면?

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect // AOP 담당 객체 (Cross Cutting Concern 로직을 정의한 객체임을 스프링 컨테이너에게 알리는 어노테이션)
@Component
public class CommonAspect {

	 * 		추가 요구 사항에 따라 Cross Cutting Concern Login을 변경
	 * 		적용 시점을 타겟 메소드 시작 전이 아니라 실행 후로 변경
	 */
	@After("execution(public * org.kosta.myproject.model.*Service.find*(..))") //
	public void execute() {
		System.out.println("**AOP** cross cutting concern : 검색 계열 메소드에 공통 작업 Ver2");
	}
}

core concern : MemberServiceImpl findMemberByAddress
AOP cross cutting concern : 검색 계열 메소드에 공통 작업 Ver2
core concern : MemberServiceImpl findMemberById
AOP cross cutting concern : 검색 계열 메소드에 공통 작업 Ver2
core concern : MemberServiceImpl register
core concern : MemberServiceImpl findMemberByName
AOP cross cutting concern : 검색 계열 메소드에 공통 작업 Ver2
core concern : ProductServiceImpl deleteProduct
core concern : ProductServiceImpl findProductById
AOP cross cutting concern : 검색 계열 메소드에 공통 작업 Ver2
core concern : ProductServiceImpl findProductByMaker
AOP cross cutting concern : 검색 계열 메소드에 공통 작업 Ver2

AOP 적용 전 예제에서는 모든 검색 계열의 메소드들을 모두 수정해야한다.
-> 유지보수성이 낮고 결합도가 높다.

AOP를 적용한 예제에서는 AOP 모듈만 수정하면된다.
-> 생산성,유지보수성이 높다.

각 클래스에 메소드에서 하던 반복적이고 공통적인 작업을 별도의 모듈에서 한번만 하고 적용하여 효율성을 높이는 작업이다.

0개의 댓글

관련 채용 정보