dev-sohee 님의 블로그

Spring의 3대 프로그래밍 모델 : 스프링 삼각형(IoC/DI, AOP, PSA) 2탄 본문

spring

Spring의 3대 프로그래밍 모델 : 스프링 삼각형(IoC/DI, AOP, PSA) 2탄

dev-sohee 2024. 8. 31. 22:42

'Spring의 3대 프로그래밍 모델 : 스프링 삼각형(IoC/DI, AOP, PSA) 1탄'에 이어서 AOP와 PSA에 대해 알아보겠습니다.

 

# AOP

AOP(Aspect-Oriented Programming)란 프로그램의 핵심 로직과 부가적인 관심사를 분리하여 코드의 중복성을 낮추고, 유지 보수성을 높이는 프로그래밍 기술입니다.  중복되는 공통 코드 부분을 별도의 영역으로 분리하고, 코드가 시행 되기 전이나 이후의 시점에 해당 코드를 붙여 넣음으로써 소스 코드의 중복을 줄이고, 필요할 때마다 가져다 쓸 수 있게 객체화하는 기술입니다.

출처_https://minaminaworld.tistory.com/153

 

구성 요소

  • 핵심 관심(Core Concerns): 시스템의 핵심 가치와 목적이 드러난 관심 영역
  • 횡단 관심(cross-cutting concern): 여러 핵심 관심에 걸쳐 공통적으로 적용되어야 하는 기능입니다.
  • Aspect: 횡단 관심사를 모듈화한 단위입니다. 예를 들어, LoggingAspect는 로깅을 처리하는 Aspect가 될 수 있습니다.
  • Join Point: Aspect가 적용될 수 있는 지점입니다. 예를 들어, 계좌 이체 메서드 호출, 입금 메서드 호출, 출금 메서드 호출, 이자 계산 메서드 호출 등이 해당됩니다.
  • Advice: Aspect가 특정 Join Point에서 수행할 작업입니다. 다양한 종류의 advice가 있으며, 예시로 Before advice, After advice , Around advice가 있습니다. 
    • Before Advice: Join Point 이전에 실행됩니다.
    • After Advice: Join Point 이후에 실행됩니다.
    • Around Advice: Join Point를 감싸서 실행됩니다.
  • Pointcut: Join Point의 부분집합이자 실제 Advice가 적용되는 부분입니다. 

아래는 은행 어플리케이션을 예로 들어 AOP의 구성 요소를 포함한 간단한 예제 코드입니다.

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

// 핵심 비즈니스 로직을 담당하는 서비스 클래스
@Service
class BankAccountService {

    // 계좌 이체 메서드
    public void transferFunds(String fromAccount, String toAccount, double amount) {
        System.out.println("Transferring " + amount + " from " + fromAccount + " to " + toAccount);
    }

    // 입금 메서드
    public void deposit(String account, double amount) {
        System.out.println("Depositing " + amount + " into " + account);
    }

    // 출금 메서드
    public void withdraw(String account, double amount) {
        System.out.println("Withdrawing " + amount + " from " + account);
    }
}

// AOP Aspect 정의
@Aspect
@Component
class LoggingAspect {

    // **Pointcut** 정의: BankAccountService 클래스의 모든 메서드에 대해 적용
    @Pointcut("execution(* BankAccountService.*(..))")
    public void bankAccountServiceMethods() {
        // 포인트컷 메서드는 구현이 없으며, 단순히 포인트컷을 정의하기 위한 메서드
    }

    // **Advice** 정의: 포인트컷에서 정의한 모든 메서드 호출 전에 실행됨
    @Before("bankAccountServiceMethods()")
    public void logBefore(JoinPoint joinPoint) {
        // **JoinPoint**: 현재 실행 중인 메서드의 정보 제공
        // 이 메서드는 포인트컷에서 정의한 메서드 호출 전에 실행됩니다.
        System.out.println("Logging before method: " + joinPoint.getSignature());
    }
}

// 스프링 설정 클래스
@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
    // 이 설정 클래스는 스프링의 컴포넌트를 검색하고 AOP를 활성화합니다.
}

// 메인 애플리케이션 실행 클래스
public class Main {
    public static void main(String[] args) {
        // Spring ApplicationContext를 초기화하고 구성 클래스를 로드
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        // BankAccountService 빈을 가져옴
        BankAccountService service = context.getBean(BankAccountService.class);

        // 핵심 비즈니스 로직 메서드 호출
        service.transferFunds("123", "456", 500.0);
        service.deposit("123", 200.0);
        service.withdraw("456", 100.0);
    }
}

 

위에서 사용된 PointCut 지시자 중 하나인 execution의 파라미터 매칭 규칙은 다음과 같습니다.

"execution(* BankAccountService.*(..))"
  • execution: 메서드 실행을 타겟으로 하는 Pointcut을 정의합니다. 즉, 어떤 메서드가 호출될 때 그 메서드를 가로채겠다는 의미입니다.
  • * : 이 부분은 메서드의 반환 타입을 지정합니다. *는 "모든 반환 타입"을 의미합니다.
  • BankAccountService: 이 부분은 가로챌 메서드들이 속한 클래스의 이름을 명시합니다. 'BankAccountService'는 대상이 되는 클래스의 이름이며, 이 클래스의 모든 메서드가 가로채기 대상이 됩니다.
  • .*(메서드 이름) : .는 클래스의 메서드를 의미하고, *는 메서드 이름을 와일드카드로 지정합니다. 즉, BankAccountService 클래스의 모든 메서드가 이 Pointcut의 대상이 됩니다.
  • (..) (메서드의 파라미터 목록): (..)는 메서드의 매개변수를 나타내며, ..는 "모든 개수와 타입의 파라미터"를 의미합니다. 즉, 메서드의 매개변수 타입과 개수에 관계없이 모든 메서드가 이 Pointcut의 대상이 됩니다.

 

# PSA

PSA(Portable Service Abstraction)란 환경의 변화와 관계없이 일관된 방식의 기술로 접근 환경을 제공하는 추상화 구조를 말합니다.

 

예를 들어, JDBC라는 표준 스펙이 있기에 오라클을 사용하든, MySQL을 사용하든, MS-SQL을 사용하든 Connection, Statement, ResultSet을 이용해 공통된 방식으로 코드를 작성할 수 있습니다. 데이터베이스의 종류에 관계 없이 같은 방식으로 제어할 수 있는 이유는 디자인 패턴에서 어댑터 패턴을 활용했기 때문입니다('객체 지향 세계로의 초대(4대 특징, 클래스 5원칙, 디자인 패턴 3가지)'을 참고해주세요). 이처럼 어댑터 패턴을 적용해 같은 일을 하는 다수의 기술을 공통의 인터페이스로 제어할 수 있게 한 것을 서비스 추상화라고 합니다. 

 

PSA는 어떤 서비스를 이용하기 위한 접근 방식을 일관된 방식으로 유지하여 어플리케이션에서 사용하는 기술이 변경되더라도 최소한의 변경만으로 변경된 요구 사항을 반영하기 위해 사용합니다. 즉, PSA를 통해서 어플리케이션의 요구 사항 변경에 유연하게 대처할 수 있습니다. Spring은 Spring Web MVC, Spring Transaction, Spring Cache, Spring Data, 메일 서비스 등을 통해 PSA를 지원하고 있습니다.

 

장점

  • 유연성: 다양한 기술과 플랫폼에서 동일한 서비스 인터페이스를 사용할 수 있습니다.
  • 확장성: 서비스의 구현을 변경하거나 확장할 때, 다른 시스템에 영향을 미치지 않습니다.
  • 유지 보수 용이: 표준화된 인터페이스를 통해 서비스의 유지 보수가 용이합니다.

단점

  • 추상화의 복잡성: 높은 수준의 추상화는 구현의 복잡성을 증가시킬 수 있습니다.
  • 성능 오버헤드: 추상화 계층이 추가되면 성능이 저하될 수 있습니다.

 

AOP와 PSA까지 공부하고 나니 스프링 프레임워크에 한발짝 더 가까워진 것 같습니다. 특히 왜 IoC/DI, AOP, PSA를 스프링 삼각형이라고 부르는지 알 것 같습니다. 이들은 서로 보완적이며, 함께 작동할 때 스프링의 핵심 가치인 효율성, 유연성, 유지 보수성, 확장성을 크게 향상시킵니다. 삼각형은 이 세 가지 개념이 스프링의 설계와 개발에서 중요한 축을 이루고 있다는 것을 시각적으로 나타내는 표현인 것 같습니다.