본문 바로가기

Backend/Java

[Java] 프록시 패턴이란?

원래 Spring 카테고리에 AOP에 대한 글을 포스팅하고 있었는데, Spring에서 제공하는 AOP를 이해하기 위해서는 기본적인 프록시 패턴에 대한 이해가 필요하기 때문에 프록시 패턴에 대한 포스팅을 먼저 진행하기로 했다.

 

우선 본 포스팅을 진행하기에 앞서 프록시 패턴에 대해 잘 설명한 reference가 있어서 참고하였다.

 

https://refactoring.guru/design-patterns/proxy

 

Proxy

There are dozens of ways to utilize the Proxy pattern. Let’s go over the most popular uses. Access control (protection proxy). This is when you want only specific clients to be able to use the service object; for instance, when your objects are crucial p

refactoring.guru

 

해당 글을 읽으면 프록시 패턴을 이해하는 것에 많은 도움이 된다.

 

프록시 패턴이란 기본적으로 코드를 설계하는 방법 즉 디자인 패턴의 일부인데, 어떠한 패턴이냐 하면 메인 object를 대리하여 어떠한 업무를 수행하고, 메인 object로의 접근을 제어하는 역할을 하는 일종의 대리인을 설계하는 패턴이다.

 

이를 구체적으로 살펴보면

 

하나의 객체가 비즈니스 로직과 데이터베이스 접근을 동시에 한다면 다수의 클라이언트의 요청으로부터 빠르고 유연하게 대처하기 힘들 것이다.

 

즉 다음 그림과 같은 상황이다.

 

이 경우에 db 접근을 하는 Object와 간단한 전후의 비즈니스 로직 정도를 처리하는 Proxy를 두어 시스템의 부하를 막는 목적으로 사용된다.

 

즉 다음과 같이 묘사할 수 있다.

 

 

위와 같이 Object와 같은 interface를 구현하는 Proxy를 설계하여 배치하게 되면 Object가 가지는 서비스의 부하를 가벼운 Proxy로 대체하며 막을 수 있다.

 

Proxy는 기본적으로 Object와 같은 interface를 구현하였기 때문에 client입장에서도 실제 같은 서비스 객체로 접근될 수 있고, 또한 Object의 전 후 처리에 무언가를 추가하고 싶어도 Object 객체의 변화 없이 수정 가능하다.

 

한번 코드로 구현해보자

 

코드로 구현해 볼 내용은 다음과 같다

 

고객에게 결제 요청이 들어왔을 때, 모든 결재요청에 대해 신용카드 잔액 확인 + 잔액이 맞으면 카드로 결재, 잔액이 부족하면 현금결재의 로직이 있다고 해보자.

 

나는 신용카드 잔액 확인 + 잔액이 맞으면 카드로 결재 까지를 Proxy로 구현을 하고

 

현금 결제에 대한 부분을 Main Object로 분리하겠다.

 

public interface Payment {

	void PayByCash(int amount);
	void PayByCreditCard(int amount);

}

 

위와 같이 간단한 Payment라는 interface를 정의하였다.

 

그다음 Proxy의 역할을 하는 CreditCard와 Object 역할을 하는 Main Object를 다음과 같이 구현하였다.

 

public class ProxyCreditCard implements Payment{

	private Payment payment;

	public ProxyCreditCard (Payment cash) {
		this.payment = cash;
	}
    
	@Override
	public void PayByCash(int amount) {
		// Proxy가 처리하지 못하는 업무

		this.payment.PayByCash(amount);
	}

	@Override
	public void PayByCreditCard(int amount) {

		if (amount > 100) {
			System.out.println("Proxy가 처리할 수 없습니다.");
			PayByCash(amount);
		}
		else {
			System.out.println(amount + "원 신용카드로 결재 By Proxy");
		}
	}
}

 

public class MainObject implements Payment{

	@Override
	public void PayByCash(int amount) {
		System.out.println(amount + "원 현금으로 결재 By Main Object");
	}

	@Override
	public void PayByCreditCard(int amount) {
		System.out.println(amount + "원 신용카드로 결재 By Main Object");
	}
}

 

그리고 실행 메인 함수를 다음과 같이 정의하였다.

 

public class Store {

	public static void main(String[] args) {
		Payment MainObj = new MainObject();
		Payment ProxyObj = new ProxyCreditCard(MainObj);

		int amount1 = 50;
		int amount2 = 150;

		ProxyObj.PayByCreditCard(amount1);
		ProxyObj.PayByCreditCard(amount2);

		ProxyObj.PayByCash(amount1);
		ProxyObj.PayByCash(amount2);
	}

}

 

잔액을 임의로 100으로 설정하고 Proxyobj에게 카드 결제를 호출하고 현금결제를 호출해보았다.

 

우선 위의 도식화 그림에 따라 모든 Client의 요청은 Proxy로 1차적으로 전달된 후, Proxy가 처리할 수 있는 업무이면 Proxy가 처리하고, Proxy가 처리할 수 없는 업무이면 Main Object에게 해당 메서드를 위임한다.

 

150원에 대한 카드결제 처리에 대한 권한이 없으므로 150원으로 카드결제를 시도하면, Main Object에게 권한이 위임될 것이다.

 

실행 결과는 다음과 같다.

 

 

위 예제와 같이 같은 Interface를 구현하는 Proxy 개체를 정의하여 간단한 로직을 구현하는 것을 프록시 패턴을 사용하여 구현하였다고 한다.

 

*저의 글에 대한 피드백이나 지적은 언제나 환영합니다.