[Spring Boot] 프록시 & AOP

diense_kk·2024년 1월 16일
0

SpringBoot

목록 보기
10/10
post-thumbnail

프록시 패턴이란?

프록시 객체는 원래 객체를 감싸고 있는 객체이다. 원래 객체와 타입은 동일하다. 프록시 객체가 원래 객체를 감싸서 client의 요청을 처리하게 하는 패턴이다.

프록시 패턴을 쓰는 이유는 접근을 제어하고 싶거나 부가 기능을 추가하고 싶을 때 사용한다.

public interface ProxyTestService {

    public void create();
    public void update();
    
}

ProxyTestService라는 인터페이스가 있고

public class ProxyTestServiceImpl implements ProxyTestService{

    @Override
    public void create(){
        System.out.println("Create");
    }

    @Override
    public void update(){
        System.out.println("Update");
    }
    
}

ProxyTestService를 구현한 ProxyTestServiceImpl이 있다고 할때 이 코드를 실행시키는 main이 client라고 볼 수 있고 ProxyTestService를 원래 객체로 볼 수 있다.

ProxyTestService를 create 메서드와 update 메서드에 동일한 기능을 하는 코드를 넣고 싶을 때, 각각 메서드에 넣어주는 것은 효율성이 떨어진다.

이럴 때 프록시패턴을 사용한다.

프록시 객체는 원래 객체와 같은 interface를 구현해줘야한다. 원래 객체를 주입받아서 interface의 메서드들을 위임받아 사용하고 원하는 추가 코드를 넣어주면 된다.

@Primary
@Service
@RequiredArgsConstructor
public class ProxyPatternService implements ProxyTestService{
    private final ProxyTestService proxyTestService;
    
    @Override
    public void create(){
        long begin = System.currentTimeMillis();
        proxyTestService.create();
        System.out.println(System.currentTimeMillis()-begin);
    }
    
    @Override
    public void update(){
        long begin = System.currentTimeMillis();
        proxyTestService.update();
        System.out.println(System.currentTimeMillis()-begin);
    }
    
}

@Primary를 사용하면 client에서 ProxyTestService를 주입받아 메서드를 호출하면 @Primary 어노테이션으로 인해 프록시 객체가 호출된다.

스프링 AOP

이렇게 해주면, 원래 코드에 손을 쓰지 않고도 기능을 추가 할 수는 있지만 프록시 객체에 중복코드가 발생할 수 있고 다른 클래스에서도 동일한 기능을 사용하고자 할 때 매번 코딩을 해줘야하는 부분에서 효율적이지 못하다.

이런 문제를 해결해주는 게 런타임시 동적으로 프록시객체를 만들어주는 것인데 그것이 AOP이다.

우선 Aspect 클래스를 만든다.

@Component
@Aspect
public class PerfAspect {
    
}

Aspect에는 2가지 정보가 필요하다. pointcut(어디에 적용할 것인지)과 advice(해야할 일, 기능)이다.

public Object logPerf 자체가 하나의 advice이며 이 advice가 어디에 적용될 것 인지(pointcut) @Around 어노테이션으로 표시해준다.
@Around("execution(* 패키지.interface.메서드)")

@Component
@Aspect
public class PerfAspect {
    
    @Around("execution(* ProxyAOP.ProxyTestService.*(..))")
    public Object logPerf(ProceedingJoinPoint pjp) throws Throwable {
        long begin = System.currentTimeMillis();
        Object retval = pjp.proceed();
        System.out.println(System.currentTimeMillis()-begin);
        return retval;
    }
    
}

execution말고 어노테이션을 만들어서 사용할 수도 있다.

어노테이션 파일을 만들고 @Retention 어노테이션을 CLASS타입으로 설정해준다.

@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface PerfLogging {
}
public class ProxyTestServiceImpl implements ProxyTestService{

    @PerfLogging
    @Override
    public void create(){
        System.out.println("Create");
    }

    @PerfLogging
    @Override
    public void update(){
        System.out.println("Update");
    }
    
}
@Component
@Aspect
@EnableAspectJAutoProxy
public class PerfAspect {

    @Around("@annotation(PerfLoggingg)")
    public Object logPerf(ProceedingJoinPoint pjp) throws Throwable {
        long begin = System.currentTimeMillis();
        Object retval = pjp.proceed();
        System.out.println(System.currentTimeMillis()-begin);
        return retval;
    }

}

0개의 댓글