Programming/Spring framework

프록시와 AOP 프로그래밍 [최범균 스프링 5] 2021-06-04

최동훈1 2021. 6. 5. 01:49

우선 스프링 공식 래퍼런스에는, AOP를 설명하기 위해 '프록시'라는 단어를 사용하고 있다.

프록시란,

간단히 추상적으로 설명하자면, 핵심기능을 제외한 공동의 기능을 한데 묶어서 코드의 변경 없이 사용할수 있도록 만든 클래스이다.

이런 말로하면 이해가 잘 되지 않을테니, 코드로서 설명하겠다.

우선, 정수 num이 주어졌을때, num의 팩토리얼을 를 구하는 기능을 가진 클래스를 2개 만들어 보자. 그런데 다형성을 사용하여 프록시를 통한 공통의 기능을 구현해야 하니, Calculator라는 인터페이스를 만들어서 상속시키자.

public interface Calculator {
	public long factorial(long num);
	

}

 다형성 사용을 위해서 인터페이스를 만들어줌.

 

public class ImpeCalculator implements Calculator {

	@Override
	public long factorial(long num) {
		// TODO Auto-generated method stub
		
		long result=1;
		for(long i=num;i>0;i--) {
			result*=i;
		}
		
		
		return result;
	}

}

반복문을 이용하여 팩토리얼 구하는 클래스 1.

 

public class RecCalculator implements Calculator{

	@Override
	public long factorial(long num) {
		// TODO Auto-generated method stub
		if(num==1) {
			return 1;
		}
		
		return num*factorial(num-1);
	}
	

}

재귀를 이용하여 팩토리얼을 구하는 클래스 2.

 

팩토리얼을 구할수 있는 기능을 가진 클래스 2개를 생성했다. 그런데 만약 각 함수마다, 실행시간을 구하고 싶으면 어떻게 할까?

가장 처음 생각나는 방법은 각 클래스의 시작과 끝에 System.currentTimemillis() 함수를 호출하여 차이를 출력하는 것이다.

public class ImpeCalculator implements Calculator {

	@Override
	public long factorial(long num) {
		// TODO Auto-generated method stub
		long start=System.currentTimeMillis();
		long result=1;
		for(long i=num;i>0;i--) {
			result*=i;
		}
		long end=System.currentTimeMillis();
		System.out.printf("ImpeCalculator.factorial(%d) 실행 시간 = %d\n",num,(end-start));
		return result;
	}

}

그런데, 이러한 방식은 만약, 구하고자 하는 단위가 밀리세컨드가 아닌, 나노세컨드로 구하려면, 각각 구현된 클래스의 코드들을 하나하나 바꿔야 하기에, 유지보수성이 매우 떨어진다. 기존 코드를 수정하지 않고, 코드 중복도 피할수 있는 방법은 없을까?

 

그럼, 각 2개의 팩토리얼을 구하는 클래스가 Calculator 인터페이스를 상속받은것을 이용한 다형성으로, "공통된" 기능인 "클래스속 factorial함수의 실행시간 구하기" 라는 기능만을 구현 한 클래스를 만들면 어떨까? 

시간단위를 바꾸기도 쉽고, 유지보수성도 뛰어날 것이다.

 

public class ExeTimeCalculator implements Calculator {

	private Calculator delegate;

	public ExeTimeCalculator(Calculator delegate) {
        this.delegate = delegate;
    }

	@Override
	public long factorial(long num) {
		long start = System.nanoTime();
		long result = delegate.factorial(num);
		long end = System.nanoTime();
		System.out.printf("%s.factorial(%d) 실행 시간 = %d\n",
				delegate.getClass().getSimpleName(),
				num, (end - start));
		return result;
	}

}

이렇게 말이다. 이런 핵심기능을 제외하고 "공동의 기능"만을 위한 클래스를 프록시라고 부른다.

이 프록시를 통해 각 함수의 실행시간을 알아보는 코드는 이렇다.

public class MainProxy {

	public static void main(String[] args) {
		ExeTimeCalculator ttCal1 = new ExeTimeCalculator(new ImpeCalculator());
		System.out.println(ttCal1.factorial(20));

		ExeTimeCalculator ttCal2 = new ExeTimeCalculator(new RecCalculator());
		System.out.println(ttCal2.factorial(20));
	}
}

 

 

핵심기능의 실행은 다른 객체에 위임하고 부가적인 기능을 제공하는 객체를 프록시(proxy)라고 부른다.

 

 

또한 실제 "핵심기능"을 실행하는 객체는 대상 객체라고 부른다. 여기서는 ImpeCalculator나 RecCalculator객체가 프록시의 대상 객체이다.

 

이렇게 공통기능 구현과 핵심기능 구현을 분리하는 것이 AOP의 핵심이다.

 

Page 158 공부완료.

 

순공부/프로그래밍 시간 1시간. 

공부시간:2시간.

 

준비만 하지말고, 시작하자. 시작한것을 꾸준히 지속하자. 점진적 과부하의 원칙은 모든 삶의 과업에 적용시킬수 있다. NOPAIN NOGAIN. 나 혼자서도 행복한 삶을 살자. 남에게 의존하지 말자.