Proxy란?

일반적으로 대리자/대리인을 의미하며, 자바에서는 타겟을 감싸서 타겟을 요청을 대신 받아주는 Wrapping Object를 뜻합니다. Proxy 패턴은 디자인 패턴의 일종으로 자바의 Reflect 패키지에 있는 Proxy를 이용하여 원래 객체의 변경 없이 기능을 특정 방식으로 제어 하는 구조를 제안합니다.

Reflection(리플렉션)에 대해서 알아보려면 이전 포스트를 참고하시기 바랍니다.

프록시 패턴의 장점

  • 목표 기능 수행 전/후에 추가적인 기능 수행(로깅, 트랜잭션, 보안 등)
  • 목표 기능 수행 과정에서 발생한 예외에 대한 처리/조작
  • 특별한 조건에 부합되는 경우 목표 기능 수행 방지
  • 목표 기능 수행 시점 변경

AOP( Aspect-Oriented Programming)란?

AOP는 한국말로 관점(관심) 지향 프로그래밍입니다. 프로젝트의 구조를 바라보는 관점을 바꾸어 프로그래밍합니다.

기능을 핵심 비지니스 로직과 공통 모듈로 구분하고, 핵심 로직에 영향을 미치지 않고 그 사이사이에 공통 모듈을 효과적으로 잘 끼워넣도록 하는 개발 방법론입니다

애플리케이션 전체에 걸쳐 사용되는 기능을 재사용하도록 하며, 프로젝트의 구조를 바라보는 관점을 바꾸어 프로그래밍한다는 점 입니다.

Proxy를 이용하여 AOP를 구현해 보도록 하겠습니다.

자바에서의 Proxy 클래스

java.lang.reflect.Proxy클래스는 동적 프록시 클래스를 만들기 위한 정적 메소드를 제공합니다.

public static Object newProxyInstance(
    ClassLoader loader,
    Class<?>[] interfaces, 
    InvocationHandler h
)
  • loader : 이 클래스 로더는 동적 프록시 클래스를 정의합니다. 클래스 로더는 동적 프록시가 생성되는 클래스 또는 인터페이스에 의해서 얻을 수 있습니다. 예) MyClass.class.getClassLoader()
  • interfaces : 두 번째 파라미터는 동적 프록시 클래스에 의해 구현 될 모든 인터페이스의 배열입니다.
  • h(andler) : InvocationHandler를 구현하고 있는 클래스의 인스턴스입니다.

자바에서의 InvocationHandler

InvocationHandler는 자바 Reflect 패키지에 포함된 인터페이스중 하나로 사용자에 의해 구현되어, 동적 프록시 클래스의 메소드를 invoke(호출) 합니다.

Object invoke(Object proxy, Method m, Object[] args)
  • proxy : 메소드가 호출될 프록시 인스턴스입니다.
  • m(ethod) : 프록시 인스턴스에서 호출되는 인터페이스 메소드에 해당합니다.
  • args : 메소드 호출에서 전달된 인자들을 포함합니다.

간단한 Dynamic Proxy 안드로이드 예제 

public class MainActivity extends AppCompatActivity{

    public static final String TAG = MainActivity.class.getSimpleName();
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Multiplier multiplier = new Multiplier2x();
        Multiplier multiplierProxy = (Multiplier) Proxy.newProxyInstance(getClassLoader(), new Class[]{Multiplier.class}, new MyProxyHandler(multiplier));
        int result = multiplierProxy.multiply(3);
        Log.e(TAG,"result = "+result);
    }
}
public interface Multiplier {
    int multiply(int value);
}
public class Multiplier2x implements Multiplier {
    @Override
    public int multiply(int value) {
        return value * 2;
    }
}

Note:안드로이드 프레임워크가 필요한 예제는 아니지만 안드로이드 프로젝트로 작성했습니다.

정수를 하나 인자로 입력 받아 multiply(곱셈)를 하는 간단한 예제입니다.
Multiplier인터페이스를 정의하였고 Multipler2x가 이를 구현하여 입력된 값의 2배를 반환합니다.

자세히 보시면 Multiplier2x의 인스턴스를 만들었지만 multiply() 호출은 프록시를 통해서 하고 있는것을 확인할 수 있습니다.

public class MyProxyHandler implements InvocationHandler {
    public static final String TAG = MyProxyHandler.class.getSimpleName();
    private Object target;
    public MyProxyHandler(Object target){
        this.target = target;
    }
    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        Log.e(TAG,"메소드 invoke 하기 전");
        int result = (int) method.invoke(target, objects);
        Log.e(TAG,"메소드 invoke 한 후");
        return result;
    }
}

MyProxyHandler는 InvocationHandler를 구현한 클래스로 타겟 오브젝트의 이벤트를 받아 대신 invoke() 해줍니다.

이와 같이 실제 이벤트가 발생하기 이전/이후에 특정 액션을 InvocationHandler를 통해 끼워넣을수 있습니다. 특정액션은 서두에 말했듯이 로깅이나  트랜잭션, Validation 등이 될 수 있습니다.

사용자의 이용 패턴 분석 예제

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

프록시의 사용 이해를 돕기위해만든 예제입니다.

사용자들의 앱 이용 패턴을 알아보고자 간단히 리싸이클러뷰를 이용하여 화면을 구성하였고, 어떤 아이템을 클릭하였을 때 프록시를 통해 진입점과 유저에 대한 정보를 서버로 전송하는 모의 예제입니다.

Google Analytics등과 함께 사용 할 수 있습니다.

public class MainInvocationHandler implements InvocationHandler {
    public static final String TAG = MainInvocationHandler.class.getSimpleName();

    private Object target;
    public MainInvocationHandler(Object object){
        target =object;
    }

    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        sendPointToEntryToServer();
        Object result = method.invoke(target, objects);

        return result;
    }
    private void sendPointToEntryToServer(){
        // TODO 진입점 클릭정보를 서버로 전송
    }
}

Conclusion

AOP를 위한 Proxy패턴은 대표적으로 서버쪽 스프링 프레임워크의 기본이 되는 개념이며, 현대 프레임워크에서 매우 중요한 역할을 담당하고 있습니다. Dagger2, Koin과 같은 Dependency Injection 프레임워크와 함께 사용한다면 모듈화를 통한 공통된 기능의 재사용성 증가로 인해 큰 효과를 볼 수 있습니다.

이해가 잘 안되더라도 기존 소스를 크게 수정하지 않고 객체의 행동을 다양한 방식으로 제어 할 수 있다는 점만 기억하시면 될 것 같습니다.

본문의 예제는 github에서 확인 가능합니다.

카테고리: Java

0개의 댓글

답글 남기기

Avatar placeholder

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.