프로그래밍 패러다임 -관점 지향(AOP)

김대경·2022년 4월 19일
0

스프링 공부 과정

목록 보기
4/6
post-custom-banner

주의사항!

공부하며 작성한 포스트입니다. 틀린 내용이나 부실한 설명이 있다면 알려주세요.😁


개요


"컴퓨팅에서 관점 지향 프로그래밍(Aspect Oriented Programming)은 횡단 관심사의 분리를 허용함으로써 모듈성을 증가시키는 것이 적인 프로그래밍 패러다임이다" - 위키백과

객체지향 설계 방식을 충분히 따르더라도, 여러 클래스에 공통된 기능이 흩어져서 존재할 수 있는데, 이렇게 흩어진 공통 기능들을 관심사 라고 한다. 이러한 관심사를 모듈화 하고, 쓰이는 곳에 필요할 때 연결함으로써 유지보수 혹은 재사용성을 더욱 더 높이는것. 그리고 OOP를 더욱 OOP스럽게 보완해주는것이 AOP이다.

위의 그림에 있는 블럭 중, 주황,파랑,빨강색의 블럭이 Aspect이다.

용어

AOP에서 사용하는 여러 용어의 의미는 다음과 같다.


  1. Aspect
    • 여러 곳에서 사용하는 코드를 모듈화 한 것.
  2. Target
    • Aspect가 적용되는 곳.
  3. Advice
    • Aspect에서 실질적인 기능에 대한 구현체.
  4. Join point
    • Advice가 Target에 적용되는 시점. (메서드 진입 시, 생성자 호출 시 등)
  5. Point Cut
    • Join Point의 상세 스펙을 정의한 것.

예제

AOP를 사용하기 전과 후의 가장 유명한 예제를 보자. 객체를 실행하고, 그 실행하는 데 걸린 시간을 출력하는 예제이다.


AOP 적용 전

public interface EventService {
	public void created();
    public void operation();
    public void deleted();
}
@Component
public class SimpleServiceEvent implements EventService{

	@override
	public void created(){
    	long begin = System.currentTimeMillis();
        try{
        	Thread.sleep(1000);
        } catch (Exception ex) {
        	ex.printStackTrace();
        }
    System.out.println("Created");
    System.out.println(System.currentTimeMillis() - begin);
    
    }
    
    @override
	public void operation(){
    	long begin = System.currentTimeMillis();
        try{
        	Thread.sleep(2000);
        } catch (Exception ex) {
        	ex.printStackTrace();
        }
    System.out.println("Operation");        
    System.out.println(System.currentTimeMillis() - begin);
    
    }
    
    @override
    public void deleted(){
    	long begin = System.currentTimeMillis();
        try{
        	Thread.sleep(3000);
        } catch (Exception ex) {
        	ex.printStackTrace();
        }
    System.out.println("Delete");
    System.out.println(System.currentTimeMillis() - begin);
    
    }

EventService 인터페이스를 만들고 구현체를 생성했다. 각 메소드는 실행하는 데 소요된 시간을 출력할것이다.
이제 이 클래스를 사용하는 코드를 생성해보자.

@Component
public class AppRunner implements ApplicationRunner {
	
    @Autowired
    EventService eventService;
    
    @Override
    public void run(ApplicationArguments args) throws Exception{
    	eventService.create();
        eventService.operation();
        eventService.delete();
    }
}

위와 같은 코드를 실행하면 아마 다음과 같은 output이 나올것이다.

Created
1006
Operation
2008
Delete
3011

이처럼 우리는 객체 지향 설계 방식을 충분히 지켰음에도 불구하고, 아래와 같은 수행 시간을 기록하는 코드는 여러 곳에서 중복되고 있다는것을 확인했다. 예제에서는 10줄 이내의 간단한 코드이지만, 인증, 로깅, 보안에 관련된 코드가 중복이 된다면, 코드가 굉장히 길어지게 될 것이고, 메소드의 수정이 일어난다면 수정 작업도 만만치 않을것이다.

중복이 발생한 코드

long begin = System.currentTimeMillis();
...
System.out.println(System.currentTimeMillis() - begin;

이제 이 코드를 Aspect를 사용해 AOP방식으로 개선하자.

AOP 적용 후

@Component
@Aspect //Aspect 정의
public class PerfAspect {
  @Around("execution(* com.example..*.EventService.*(..))") // 해당 패키지에 적용
  // Advice 정의 (*Around -> 메서드 실행 전, 후 실행)
  public Object logPerf(ProceedingJoinPoint pjp) throws Throwable {
    long begin = System.currentTimeMillis(); // 메소드 실행 전에 작동
    Object retVal = pjp.proceed(); // proceed() -> 메소드 실행 *Around 사용 시 반드시 있어야 함.
    System.out.println(System.currentTimeMillis() - begin); //메소드 실행 후 작동
    return retVal;
  }
}

위와 같은 Aspect를 정의하고, 중복이 발생한 코드를 지우면 다음과 같은 코드가 된다.

@Component
public class SimpleServiceEvent implements EventService{

	@override
	public void created(){
        try{
        	Thread.sleep(1000);
        } catch (Exception ex) {
        	ex.printStackTrace();
        }
    System.out.println("Created");
    
    }
    
    @override
	public void operation(){
        try{
        	Thread.sleep(2000);
        } catch (Exception ex) {
        	ex.printStackTrace();
        }
    System.out.println("Operation");        
    
    }
    
    @override
    public void deleted(){
        try{
        	Thread.sleep(3000);
        } catch (Exception ex) {
        	ex.printStackTrace();
        }
    System.out.println("Delete");
    
    }

다시 AppRunner를 실행해보면, 동일한 결과가 나오게 되는것을 알 수 있다.

사용법

Aspect를 생성하는 과정에서 처음 보는 문법이 있을텐데, 제일 처음 보이는 @Around는 어드바이스이다. 메소드가 무엇을, 언제 할지 의미하고있습니다. 여기서 "무엇"이란 logPerf() 메소드입니다.
"언제"는 @Around가 되는데, "언제"의 경우, 총 5가지의 타입이 존재한다.

시점설명
@Before메소드가 호출되기 전에 기능을 수행
@After메소드의 결과에 관계 없이, 메소드가 완료되면 수행
@Around메소드 호출 전,후 기능 수행
@AfterReturning메소드가 성공적으로 결과를 반환하면 수행
@AfterThrowing메소드 실행 중 예외를 던지게 되면 수행

파란 박스의 "execution(* com.blogcode.board.BoardService.getBoards(..))"는 어드바이스의 value이다. 이 문자열을 포인트컷 표현식이라고 하는데, 포인트컷 표현식은 지정자, 타겟명세 둘로 나눌 수 있고, 총 9가지가 있지만, Annotation, execution방식을 가장 많이 사용한다.

지정자설명
execution()접근제한자, 리턴타입, 인자타입, 클래스/인터페이스, 메소드명, 파라미터타입, 예외타입 등을 전부 조합가능한 지정자
@annotation타겟 메소드에 특정 어노테이션이 지정된 경우

효과

AOP를 적용함으로써, 중복이 발생한 코드를 지울 수 있게 된 것이다. 그리고 기존에는 각 함수의 기능 외에도 실행 시간 측정이라는 책임을 맡게 됨으로써, 단일 책임 원칙에도 어긋났었던 코드를 좀 더 간결하고 본인의 역할에만 집중할 수 있게 만들 수 있다..

출처

https://jojoldu.tistory.com/71?category=635883

profile
무언가를 개발한지 4년이 되어가는 중입니다 :) With @KSASolution
post-custom-banner

0개의 댓글