AOP와 프록시 빈

KKH_94·2023년 7월 12일
0

Spring_Framework

목록 보기
6/8

스프링에서 AOP(Aspect-Oriented Programming)은 프로그램의 핵심 로직과는 별개로 발생하는 부가적인 기능들을 모듈화하여 처리하는 프로그래밍 패러다임입니다.

AOP는 프로그램의 여러 부분에서 반복적으로 나타나는 핵심 로직 외의 공통 관심 사항을 분리하여 재사용하고, 이를 통해 코드의 모듈성과 유지보수성을 향상시킬 수 있습니다.

AOP의 주요 구성 요소는 다음과 같습니다.

  • Aspect(관점) : 관점은 공통 관심 사항을 구현한 모듈입니다.

    예를 들어, 로깅, 트랜잭션 관리, 보안 등이 관점의 예입니다. 각 관점은 하나 이상의 어드바이스(Advice)와 포인트컷(Pointcut)으로 구성됩니다.

  • Advice(어드바이스) : 어드바이스는 관점의 동작을 구체적으로 정의한 것입니다. 어드바이스는 핵심 로직의 특정 시점에 실행될 수 있습니다.

    예를 들어, 핵심 로직 실행 전에 수행되는 어드바이스를 "Before Advice"라고 합니다. 다른 유형의 어드바이스로는 "After Advice", "Around Advice" 등이 있습니다.

  • Pointcut(포인트컷) : 포인트컷은 어드바이스가 실행될 지점을 결정하는 방법을 정의합니다. 포인트컷은 핵심 로직에서 특정 메서드, 클래스, 패키지 등을 지정하여 관심사항을 선택합니다.

    예를 들어, 모든 서비스 계층의 메서드에 대해 트랜잭션을 적용하려면 포인트컷으로 해당 메서드를 선택할 수 있습니다.

  • Join point(조인 포인트) : 조인 포인트는 프로그램 실행 중에 관점이 적용될 수 있는 특정한 지점을 말합니다.

    메서드 실행, 예외 발생, 필드 접근 등이 조인 포인트의 예입니다.

  • Weaving(위빙) : 위빙은 AOP를 적용하여 관점을 핵심 로직에 주입하는 과정입니다.

    이 과정에서 프록시 객체가 생성되거나 바이트 코드 조작이 수행됩니다. 스프링은 런타임에 위빙을 수행하는 프록시 기반의 AOP를 주로 사용합니다.

스프링에서 AOP를 구현하기 위해 다양한 방법을 사용할 수 있습니다. 가장 일반적인 방법은 스프링 AOP를 이용하는 것입니다.

스프링 AOP는 프록시 기반의 AOP를 구현하며, JDK 프록시 또는 CGLIB 프록시를 사용하여 핵심 로직에 관점을 주입합니다.


스프링 AOP를 사용하기 위해 다음과 같은 단계를 거칩니다.

  • 관점(Aspect)을 작성합니다. 관점은 @Aspect 어노테이션을 사용하여 선언하며, 포인트컷과 어드바이스를 정의합니다.

  • 포인트컷(Pointcut)을 정의합니다. 포인트컷은 @Pointcut 어노테이션을 사용하여 선언하며, 특정한 메서드 또는 패턴을 지정하여 관심사항을 선택합니다.

  • 어드바이스(Advice)를 작성합니다. 어드바이스는 @Before, @After, @Around 등의 어노테이션을 사용하여 선언하며, 관심사항을 구체적으로 정의합니다.

  • AOP 설정을 수행합니다. 스프링 설정 파일(XML 또는 Java Config)에서 aop:aspectj-autoproxy 요소를 추가하여 AOP를 활성화합니다.

위의 단계를 거치면 스프링은 관점을 핵심 로직에 주입하여 AOP를 적용합니다.

핵심 로직이 실행될 때, 관점에 정의된 어드바이스가 포인트컷에 맞는 조인 포인트에서 실행됩니다.

이렇게 스프링에서 AOP를 사용하면 공통된 기능을 한 곳에서 관리할 수 있고, 핵심 로직과의 결합도를 낮춰 유지보수성을 향상시킬 수 있습니다.

AOP는 트랜잭션 관리, 로깅, 보안 등의 측면에서 많이 활용되며, 코드의 재사용성과 가독성을 향상시킬 수 있는 강력한 도구입니다.



스프링에서 프록시 빈은 프록시 디자인 패턴을 사용하여 빈 객체를 감싸는 래퍼(Wrapper) 객체입니다. 프록시는 원본 객체에 대한 접근을 제어하고, 추가적인 기능을 제공하거나 작업을 수행하기 위해 사용됩니다.

프록시 빈은 주로 AOP (Aspect-Oriented Programming)와 함께 사용되어, 보안, 트랜잭션 관리, 로깅 등과 같은 공통된 기능을 수행하는 데에 활용됩니다. 이러한 기능들은 애플리케이션의 여러 위치에서 중복적으로 사용되는 경우가 많기 때문에, 프록시 빈을 통해 이를 중앙에서 관리하고 재사용할 수 있습니다.

스프링에서 프록시 빈은 대부분 자동으로 생성됩니다. 프록시 빈은 원본 빈과 동일한 인터페이스를 구현하거나 동일한 부모 클래스를 상속받는 형태로 생성됩니다. 스프링은 빈 설정 정보를 분석하여, 프록시 빈을 자동으로 생성하고 필요한 기능을 적용합니다.

프록시 빈을 사용하면, 애플리케이션 코드에서는 실제 원본 빈 대신 프록시 빈을 주입받아 사용할 수 있습니다.

이렇게 함으로써 프록시 빈은 애플리케이션 코드에 영향을 주지 않으면서, 필요한 추가 기능을 적용할 수 있습니다. 프록시는 원본 객체의 메소드 호출 전후에 추가적인 동작을 수행할 수 있으며, 필요에 따라 원본 객체의 메소드를 호출하지 않거나, 결과를 가공하여 반환할 수도 있습니다.

스프링은 다양한 프록시 기술을 지원합니다. 가장 일반적으로 사용되는 프록시 방식은 JDK 다이나믹 프록시와 CGLIB 프록시입니다. JDK 다이나믹 프록시는 인터페이스를 기반으로 프록시를 생성하며, CGLIB 프록시는 클래스를 상속받아 프록시를 생성합니다. 스프링은 이러한 프록시 방식 중에서 적절한 방식을 선택하여 프록시 빈을 생성합니다.

요약하면, 스프링에서 프록시 빈은 기존 빈을 감싸고, 애플리케이션 코드에서는 프록시 빈을 사용하여 필요한 추가 기능을 적용하는 데에 활용됩니다. 이를 통해 코드의 재사용성과 모듈성을 향상시킬 수 있으며, 공통된 기능을 중앙에서 관리하여 코드 중복을 방지할 수 있습니다.


아래는 스프링에서 프록시 빈을 사용하는 간단한 예제 코드입니다.

이 예제에서는 serService인터페이스를 구현한 serServiceImpl클래스에 대해 프록시를 생성하여 트랜잭션 관리를 추가하는 예제입니다.

// UserService 인터페이스
public interface UserService {
    void saveUser(User user);
}

// UserServiceImpl 클래스
public class UserServiceImpl implements UserService {
    public void saveUser(User user) {
        // 사용자 저장 로직
    }
}

// TransactionalProxy 프록시 클래스
public class TransactionalProxy implements UserService {
    private UserService userService;
    
    public TransactionalProxy(UserService userService) {
        this.userService = userService;
    }
    
    public void saveUser(User user) {
        // 트랜잭션 시작
        // 추가적인 작업 수행
        
        userService.saveUser(user);
        
        // 트랜잭션 종료
        // 추가적인 작업 수행
    }
}

// Spring 빈 설정
@Configuration
public class AppConfig {
    @Bean
    public UserService userService() {
        return new TransactionalProxy(new UserServiceImpl());
    }
}

// 사용 예제
public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        UserService userService = context.getBean(UserService.class);
        
        User user = new User("John", "Doe");
        userService.saveUser(user);
    }
}

위의 예제에서 UserServiceImpl은 실제 사용자 저장 로직을 담당하는 클래스입니다. TransactionalProxyUserService 인터페이스를 구현하며, 생성자로 UserService 구현체인 UserServiceImpl을 받아들입니다.

saveUser 메소드에서는 트랜잭션을 시작하고, 추가적인 작업을 수행한 뒤 userService.saveUser(user)를 호출하여 원본 메소드를 실행한 후 트랜잭션을 종료하고 추가적인 작업을 수행합니다.

AppConfig 클래스는 스프링의 빈 설정을 담당합니다. userServic 빈은 TransactionalProxy 인스턴스를 생성하고, UserServiceImpl을 주입받아 프록시를 생성합니다.

Main 클래스에서는 AppConfig를 사용하여 ApplicationContext를 생성하고, UserService 빈을 가져온 후 saveUser 메소드를 호출합니다. 이때 실제로 호출되는 것은 프록시를 통해 감싸진 ransactionalProxy saveUser메소드입니다.

profile
_serendipity

0개의 댓글