📖 proxy[prɒk.si]
선거에서 다른 사람을 위해 투표하는 등 다른 사람을 위해 행동할 수 있는 권한 또는 이 권한이 부여된 사람
출처 - cambridge 사전
프록시는 사전적인 의미로 대리인을 의미한다. 사전적인 의미와 객체지향의 관점으로 바라보면 특정 객체에 직접 접근하지 않고 대리 객체를 통해 원하는 객체에 접근 할 것이라고 추론 할 수 있다. 프록시 패턴을 이용하면 클라이언트카 특정 객체의 오퍼레이션에 접근하기전에 프록시 객체를 지나 접근 하도록 하는 패턴이다. 프록시 패턴은 클라이언트로 부터 타겟 객체의 복잡성을 커버하기 위해 Wrapper를 생성해야 할 때 사용되며. 이를 이용한 예시로는 캐시와 로깅이 있고, 또한 프록시에 보안을 적용해 해당객체에 접근제어를 하도록 하거나 성능 측정을 하는 등의 방법이 있다.
만약 보안이나 성능측정과 같은 방식들을 접근 하고자 하는 객체의 메서드로 구현한다면 객체의 코드는 복잡해지고 다른 기능을 갖게되어 단일 책임 원칙(Single Responsibility Principle)을 위반하게 될 것이다. 때문에 프록시 패턴을 사용하면 이를 어기지 않고 사용할 수 있을 것이다.
프록시 패턴을 이용한 다른 예시로는 생성하는데 많은 리소스를 필요하는 인스턴스를 미리 생성하거나 Lazy initialization를 이용해 사용 시 구현 할 수 있다. 또한 프록시를 통해 로깅이나 캐싱과 같은 개념을 구현할 수 있다.
스프링의 AOP가 프록시패턴으로 구현되어있다.
위 클래스 다이어그램을 통해 구현해보자.
// proxy
public class GameServiceProxy extends GameService {
@Override
public void startGame() throws InterruptedException {
long before = System.currentTimeMillis();
super.startGame();
System.out.println(System.currentTimeMillis() - before);
}
}
.
.
// service
public class GameService {
public void startGame() throws InterruptedException {
System.out.println("Game Start!");
Thread.sleep(1000L);
}
}
.
.
// client
public class Client {
public static void main(String args[]) throws InterruptedException {
GameService gameService = new GameServiceProxy();
gameService.startGame();
}
}
위와 같은 코드는 더 이상의 service를 만들기 불편하기 때문에 interface를 이용해 클래스 다이어그램에 따라 만들어 보았다.
// interface
public interface GameService {
void startGame() throws InterruptedException;
}
.
.
// interface 구현체 - game service
public class DefaultGameService implements GameService{
@Override
public void startGame() throws InterruptedException {
Thread.sleep(1000L);
System.out.println("Game Start!");
}
}
.
.
// interface 구현체 - proxy
public class GameServiceProxy implements GameService{
private GameService gameService;
@Override
public void startGame() throws InterruptedException {
long before = System.currentTimeMillis();
if (this.gameService == null ) {
this.gameService = new DefaultGameService();
}
gameService.startGame();
System.out.println(System.currentTimeMillis() - before);
}
}
.
.
// client
public class Client {
public static void main(String args[]) throws InterruptedException {
GameService gameService = new GameServiceProxy();
gameService.startGame();
}
}
프록시 패턴을 이용하여 접근 제한 프록시를 구현해 볼 것이다.
// Proxy
public class ProxyInternet implements Internet{
private Internet internet;
private static List<String> bannedSite;
static {
bannedSite = new ArrayList<>();
bannedSite.add("illegal.com");
bannedSite.add("bat777.com");
}
public ProxyInternet(Internet internet) {
this.internet = internet;
}
@Override
public void connectTo(String serverHost) throws Exception {
if (bannedSite.contains(serverHost.toLowerCase())) {
throw new Exception("Access Denied");
}
internet.connectTo(serverHost);
}
}
.
.
// Client
public class Client {
public static void main(String args[]) throws Exception {
Internet site = new ProxyInternet(new RealInternet());
// connect
site.connectTo("nabor.com");
site.connectTo("bat777.com");
}
}
Internet 인터페이스와 구현체는 생략하였다. 특정 사이트에 대해 접근 제한할때 다음과 같이 만들 수 있다.
Over time, Aaron’s casual approach began to yield unexpected results. The site’s detailed game analytics and interactive tutorials helped him refine his strategies and improve his skills. As he continued to play, his modest bets started https://winuniquecasino-france.com/ to turn into substantial wins. Aaron’s story is a testament to the idea that even those who start as amateurs can achieve remarkable success with persistence and the right resources. His journey from a casual player to an impressive winner underscores how dedication and the right tools can transform one’s gaming experience.