프록시 패턴

오늘은 스프링 AOP에 대해서 공부했던 과정들을 숙지하기 위해서 다시한번 프록시에 대해서 알아보았습니다. 프록시에 대해서 검색을 하는 도중에 프록시 패턴에 대한 포스팅을 접하게 되었고, 이 내용은 실제 AOP가 어떻게 동작하는지에 대한 메커니즘을 이해하는데 좋은 글이였다고 생각하여 다시 반복적인 코드 작성을 통하여 알아보는 시간이 였습니다.

프록시란?

프록시는 실제로 액션을 취하는 객체를 대신해서 대리자 역할을 합니다. 한마디로 실제 호출해야하는 메서드를 가지고 있는 실제 대상을 감싸고 있는 Wrapping 클래스라고 생각하시면 됩니다.

프록시 패턴을 사용하게 되면 프록시 단계에서 권한을 부여할 수 있는 이점이 생기고 필요에 따라 객체를 생성시키거나 사용하기 때문에 메모리를 절약할 수 있는 이점도 생깁니다. 프록시 패턴이 하는 일은 한마디로 자신이 보호하고 있는 객체에 대한 엑세스 권한을 제어하는 것입니다.

이렇게 함으로써 실제 인스턴스 사용 과정을 관여하고 메모리를 절약하는 방법을 코드로 살펴보겠습니다.

가장 먼저 실질적으로 사용되는 핵심 코드를 만들어보겠습니다. 하나는 인터페이스이고 하나는 인터페이스를 상속받는 클래스입니다.

인터페이스

public interface CommandExcutor {

    public void runCommand(String cmd) throws Exception;

}

인터페이스를 구현한 클래스

public class CommandExcutorImpl implements CommandExcutor {

    @Override
    public void runCommand(String cmd) throws Exception {
        Runtime.getRuntime().exec(cmd);
        System.out.println("cmd: " + cmd);
    }
}

위의 코드를 보면 인터페이스와 구현을 분리하여 작성하였고, 실질적으로 CommandExcutorImple를 호출하여 객체를 생성시키고 높은 cost의 runCommand() 메서드 작업으로 인해 메모리 낭비가 예상될 수 있습니다.

Runtime.getRuntime().exec() 메서드는 process를 실행시키거나 os를 제어할때 사용하는 클래스로만 알고 있습니다. 이 부분에 대해서는 저도 공부를 하지 않았기 때문에 따로 설명드리진 않겠습니다.

proxy class

위의 단점을 보완하기 위해 프록시 클래스를 작성하였습니다. 일단 똑같은 인터페이스를 상속받음으로 인터페이스의 일관성을 유지합니다.
그리고 생성자에서 CommandExcutorImpl 클래스를 인스턴스화 시키고, 인스턴스화 된 excutor 객체의 runCommand() 메서드를 프록시 클래스의 runCommand 메서드에서 엑세스를 결정합니다.

public class CommandExcutorProxy implements CommandExcutor {

    // isAdmin 값에 따라서 객체에 대한 엑세스 권한을 제어합니다.    
    private boolean isAdmin;
    private CommandExcutor excutor;


    public CommandExcutorProxy(String user, String pwd) {
        if ("sa1341".equals(user) && "1234".equals(pwd)) {
            isAdmin = true;
        }
        excutor = new CommandExcutorImpl();
    }


    @Override
    public void runCommand(String cmd) throws Exception {

        if (isAdmin) {
            excutor.runCommand(cmd);
        } else {
            if (cmd.trim().startsWith("rm")) {
                throw new Exception("rm is only admin");
            } else {
                excutor.runCommand(cmd);
            }
        }
    }
}

실행 클래스

public class ProxyPatternTest {

    public static void main(String[] args) {

        CommandExcutor excutor = new CommandExcutorProxy("sa1341","1111");

       try {
           excutor.runCommand("ls -al");
           excutor.runCommand("rm -rf *");
       }catch (Exception e){
           System.out.println(e.getMessage());
       }


    }
}

위에서는 일부로 패스워드를 다르게 주어서 특정 명렁어에 대해서 예외를 던지도록 작성하였습니다. rm -rf * 같이 터미널에서 디렉토리를 전부 강제 삭제하는 명령어이기 때문에 리스크가 존재하기 때문에 인증된 사용자만이 수행할 수 있도록 테스트 케이스에 넣어봤습니다.

실행 결과

스크린샷 2019-11-20 오후 7 33 15

위와 같이 프록시를 작성하면 인터페이스를 일관성있게 유지시키면서 엑세스 권한을 부여할 수 있고 더불어 메모리 절약을 할 수 있게 됩니다.

참조: https://blog.seotory.com/post/2017/09/java-proxy-pattern

'SpringFramework > JAVA' 카테고리의 다른 글

람다식을 통한 메소드 참조  (0) 2019.12.03
제네릭을 사용하는 이유?  (1) 2019.12.03
Object- 1장 객체, 설계  (0) 2019.11.04
Stream(스트림)  (0) 2019.10.05
@Annotation 이란?  (0) 2019.10.05

+ Recent posts