Java/Design Pattern

[디자인 패턴] 구조 패턴 - 프록시 패턴 (Proxy Pattern)

민돌v 2023. 3. 24. 10:41
728x90
(인프런) 코딩으로 학습하는 GoF의 디자인 패턴 - 백기선, 강의를 보고 정리한 글입니다.
코드는 GitHub 에 있습니다

 

#1. 객체 생성 관련 패턴

#2. 구조 관련 패턴

 

#3. 행동 관련 패턴

 

 

 


✔️ 프록시 패턴이란 (Proxy Patterns)

특정 객체에 대한 접근을 제어하거나 기능을 추가할 수 있는 패턴입니다.
  • 프록시(Proxy)란 대리자, 대변인의 의미를 가지고 있습니다. 즉 누군가를 대신해서 그 역할을 수행하는 존재이고 프로그래밍에서도 같은 의미로 프록시에게 어떠한 일을 대신 시키는 것을 말합니다.
  • 프록시 패턴은, 어떠한 객체에 대해 접근하기 전에 프록시 객체를 거쳐 "초기화 지연, 접근 제어, 로깅, 캐싱" 등 다양한 기능을 응용해서 사용할 수  있도록 하는 구조입니다.
  • 즉, 어떠한 객체에대해 직접 참조하지 않고 무엇인가의 행위를 그 전에 하고싶을 때 프록시 패턴의 구조를 사용하면 유용하다고 볼 수 있을 것 같습니다.

 

proxy pattern 구조

 

 


✔️ 프록시 패턴 사용 예시

단순하게 Proxy Pattern 의 개념만 적용한 예시 입니다.
  • 아래처럼 어떤 로직을 수행하는 Service 객체가 존재할 때, 이 객체가 수행하는 로직에대해 시간을 측정하고 싶다면
  • startGame() 이라는 메서드를 직접 수정해서 "시간 측정"에 대한 기능을 추가할 수 있습니다.
public class Client {

    public static void main(String[] args) {
        GameService gameService = new GameService();
        gameService.startGame();

    }
}

public class GameService {

    public void startGame() {
        //엄청난 비지니스 로직
    }
}

 

하지만 그렇게 요구사항이 생길 때 마다 로직을 직접적으로 수정하고 변경할 수 없을때도 존재하고(Side-Effect), 객체지향 원칙 중 OCP 에도 위반됩니다.

따라서 우리는 프록시 패턴을 적용하여 "시간 측정"이라는 기능을 대신 수행할 수 있습니다.

 

[프록시 패턴 적용]

  • Client는 객체를 직접 접근하는게 아닌, 프록시 객체를 통해 접근합니다.
  • 프록시 객체는 컨택스트 객체의 기능을 참조하면서, "시간 측정" 이라는 기능을 대신 수행합니다.
  • 이로써 기존의 컨택스트 객체의 starGame() 은 전혀 수정하지않고 프록시 객체가 대신 원하는 기능을 수행하는 구조가 되었습니다.
public class Client {

    public static void main(String[] args) throws InterruptedException {
        GameService gameService = new GameServiceProxy(new DefaultGameService());
        gameService.startGame();

    }
}

public class GameServiceProxy extends GameService {

    @Override
    public void startGame() throws InterruptedException {
        long before = System.currentTimeMillis();
        super.startGame();
        System.out.println(System.currentTimeMillis() - before);
    }
}

 

[상속이 아닌 위임]

  • 상속이 아닌 위임의 방식으로 프록시 패턴을 적용시킬 수 있습니다.
//Client
public class Client {

    public static void main(String[] args) throws InterruptedException {
        GameService gameService = new GameServiceProxy(new DefaultGameService());
        gameService.startGame();

    }
}

//인터페이스로 수정해 더 유연하게 코드를 변경할 수 있음 (복잡하지만)
public interface GameService {
    void startGame() throws InterruptedException;
}

//Context Class
public class DefaultGameService implements GameService{

    @Override
    public void startGame() throws InterruptedException {
		//엄청난 비지니스 로직 매우매우 오래걸림
	}
}
  • 상속이 아닌 위임을 통해 ocp 를 지키고 확장성을 증가시킬 수 있습니다.
public class GameServiceProxy implements GameService {

    private GameService gameService;

    public GameServiceProxy(GameService gameService) {
        this.gameService = gameService;
    }

    @Override
    public void startGame() throws InterruptedException {
        long before = System.currentTimeMillis();
        gameService.startGame();
        System.out.println(System.currentTimeMillis() - before);
    }
}

 

프록시로 초기화 지연 사용하기

  • 상속이 아닌 위임의 구조로 프록시 패턴을 적용하면 초기화 지연기능 또한 쉽게 구현할 수 있습니다.
public class Client {

    public static void main(String[] args) throws InterruptedException {
        GameService gameService = new GameServiceProxy();
        gameService.startGame();
    }
}


public class GameServiceProxy_Plus implements GameService {

    private GameService gameService;

    @Override
    public void startGame() throws InterruptedException {
        long before = System.currentTimeMillis();

        //이런식의 lazy 로딩도 가능함
        if (this.gameService == null) {
            this.gameService = new DefaultGameService();
        }

        gameService.startGame();
        System.out.println(System.currentTimeMillis() - before);
    }
}

 


✔️ 프록시 패턴 장단점

장점

  1. 기존 코드를 변경하지 않고 새로운 기능을 추가할 수 있다.
  2. 기존 코드는 해야 하는 일만 유지할 수 있다.
  3. 기능 추가 및 초기화 지연 등으로 다양하게 활용할 수 있다.
  4. 실제 객체의 public, protected 메소드를 숨기고 인터페이스를 통해 노출시킬 수 있다.

 

단점

  1. 객체를 생성할 때 한 단계를 거치게 되므로, 빈번한 객체 생성이 필요한 경우 성능이 저하될 수 있다.
  2. 프록시 내부에서 객체 생성을 위해 스레드가 생성, 동기화가 구현되어야 하는 경우 성능이 저하될 수 있다.
  3. 로직이 난해해져 가독성이 떨어질 수 있다.

✔️ JAVA 와 Spring 에서의 프록시 패턴

자바

  1. 다이나믹 프록시
  2.  java.lang.reflect.Proxy

 

스프링

  1. 스프링 AOP

 

 

 

 

 

반응형