Spring Framework

관점지향프로그래밍(AOP) 에서 관점(Aspect)은 무슨 뜻일까?

옷덕 2024. 2. 13. 10:19

스프링 프레임워크의 주요 특징은 무엇인가? 를 얘기하면 빠지지않게 나오는것 중 하나인 관점지향프로그래밍(Aspect Orient Programing)에 관한 글입니다. 바로 들어가죠.

 

AOP에서  말하는 Aspect 란?

 

위 그림에서 사용자, 개발자, 운영자는 각각 자기만의 관점이 있습니다. 여기서 사용자의 관점이 주 업무로직이 되고, 개발자&운영자가 원하는 부분은 사이드적인 부분이라 볼 수 있겠죠.

 

이처럼 프로그램을 만들며 사용자가 요구한 로직 뿐아니라, 개발자나 운영자에게 필요한 로직도 만들어야 하는데 '관점 지향 프로그래밍'에서 '관점(Aspect)'은 이러한 부분들을 말합니다.

즉 이러한 '코어 업무 외 의 업무들을 주업무 로직과 어떻게 분리하고, 결합시킬것인가' 에 대한 방법론이 AOP 라고도 말할 수 있겠습니다.

 

로그처리, 보안처리, 트랜잭션 처리 같은것들은 코어업무와 동떨어지지만 다른 관점에서 필요한 업무들 입니다. 다른 관점이죠. 

 

위 그림에서 컨테이너 박스는 주업무로 만들어진 객체들(Primary Concern)이고, 노란색 선들이 다른 관점의 업무들(Cross-cutting Concern)을 나타냅니다. 맨 왼쪽의 원은 Proxy 클래스를 나타냅니다.

 

로그처리, 보안처리 등을 잘 생각해보면 주 메소드들의 위와 아래에 껴들어가게 된다. 프로그램의 흐름과 크로스로 뺐다&꼈다 해야하는(위 그림을 보며 개념을 시각화 해봅시다) 로직들이기 때문에 이러한 다른 관점의 코드들을 Cross-cutting Concern 이라고 부릅니다.

 

그렇다면 주 업무가 진행되는 와중에 로그를 보고 싶다면 어떻게 해야할까요? 그때 마다 코드를 수정 해야 할까요?

 

이렇게 관점이 다른 코드들을 쉽게 뺐다, 꽂았다 할 수 있는 방법을 생각하다 나온 방법론이 AOP 입니다.

 

예제로 개발자를 위한 로그가 섞여있는 코드를 보고 가겠습니다.

 

public class NewlecExam implements Exam {
    public int total() {
        long start = System.currentTimeMillis();
        SimpleDateFormat dayTime = new SimpleDateFormat("yyyy-mm-dd hh:mm:ss");
        String str = dayTime.format(new Date(start));
        System.out.println(str); // 현재 시간 출력을 출력하는 부업무(로그)

        /*--------------------------------*/

        int result = kor + eng + math + com; //주 업무

        /*--------------------------------*/

        long end = System.currentTimeMillis();
        String message = (end - start) + "ms 걸림";
        System.out.println(message); 

        return result;
    }

}

 

이처럼 주업무의 목적(관점)과 다른 목적(로깅)을 가진 코드가 섞여 있으면 코드도 복잡해지고, 유지보수 측면에서도 힘들어지겠죠.

 

그렇기 때문에 분리가 필요합니다.

 

AOP의 구현은 Core Concern과 Cross-cutting concern을 분리하고 Proxy를 거쳐서 함수가 호출되도록 하는 방식으로 구현하게 됩니다. 주업무와 다른 관점들을 분리시킨 코드를 볼까요?

 

import org.springframework.stereotype.Component;

@Component
public class NewlecExam implements Exam {
    private int kor, eng, math, com; // 실제 값이 할당되어야 함

    // 주 업무 로직만 남김
    public int total() {
        // 성적 합산 로직
        int result = kor + eng + math + com;
        return result;
    }

    // Setter 메소드는 생략했습니다.
}

 

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

@Aspect
@Component
public class PerformanceLoggingAspect {

    // 주업무 메소드에 대한 Pointcut 정의
    @Pointcut("execution(* NewlecExam.total(..))")
    public void performanceLog() {}

    // 주업무 메소드 실행 전에 호출
    @Before("performanceLog()")
    public void logBefore(JoinPoint joinPoint) {
        long start = System.currentTimeMillis();
        SimpleDateFormat dayTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String str = dayTime.format(new Date(start));
        System.out.println("Start time: " + str);
        LoggingUtil.startTime.set(start); // 시작 시간을 스레드 로컬에 저장
    }

    // 주업무 메소드 실행 후에 호출
    @After("performanceLog()")
    public void logAfter(JoinPoint joinPoint) {
        long end = System.currentTimeMillis();
        long start = LoggingUtil.startTime.get(); // 스레드 로컬에서 시작 시간 가져오기
        String message = (end - start) + "ms 걸림";
        System.out.println("End time: " + message);
        LoggingUtil.startTime.remove(); // 스레드 로컬에서 시작 시간 제거
    }
}

// 스레드별로 시작 시간을 관리하기 위한 유틸리티 클래스
class LoggingUtil {
    static ThreadLocal<Long> startTime = new ThreadLocal<>();
}

 

@Pointcut 어노테이션을 사용해 로깅이 필요한 메소드를 지정하고, @Before@After 어노테이션으로 메소드 실행 전후의 로깅 로직을 지정합니다. 

 

이렇게 주업무 로직과 부업무 로직을 분리하여 추후에 다른 부업무 로직(주로 로깅)이 생기더라도 주업무를 담당하는 코드를 수정하지 않고 추가할 수 있게 됩니다. 확장, 유지보수가 쉬워지는 장점이 있죠.

 

위의 Proxy 를 통한 코드분리 방법은 사실 스프링에서만 가능한게 아니라 자주 쓰이는 리펙토링 방법(디자인패턴)중 하나 입니다. (스프링, 자바를 떠나 다른 언어, 프레임워크에도 적용 가능합니다)

https://refactoring.guru/ko/design-patterns/proxy 

 

프록시 패턴

/ 디자인 패턴들 / 구조 패턴 프록시 패턴 다음 이름으로도 불립니다: Proxy 의도 프록시는 다른 객체에 대한 대체 또는 자리표시자를 제공할 수 있는 구조 디자인 패턴입니다. 프록시는 원래 객체

refactoring.guru

 

이러한 기법을 Spring 프레임워크는  Annotation 을 통해( IOC컨테이너를 통한 DI) AOP를 손쉽게 구현할 수 있도록 도와주기 때문에 꾸준히 사랑받을 수 있는거죠. 

 

여기까지 Spring 의 주요 특징중 하나인 AOP 에 관한 설명이었습니다. 감사합니다!