출처 : https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4
프록시 (Proxy)
- 특정 객체에 대한 접근을 제어하거나 기능을 추가할 수 있는 패턴.
어디에 쓸 수 있는가
- 해당 객체의 접근 제어
- 객체에 대한 보안 - 특정 유저의 권한 유무를 확인하여, 특정 객체에 접근 가능/불가능을 판별한다.
- 로깅, 캐싱
- 생성하는 데 많은 리소스를 요구하는 객체를 실제로 쓰일 때 생성하는 초기화 지연 방식으로 리소스를 아낀다.
왜 쓰는가
- 기존 코드를 전혀 건드리지 않고 특정 기능을 추가
- 기존 코드를 건드리지 못할 때 특정 기능을 추가
장점
- 기존 코드를 변경하지 않고 새로운 기능을 추가할 수 있다.
- 기존 코드가 해야하는 일만 유지할 수 있다.
- 기능 추가 및 초기화 지연 등으로 다양하게 활용할 수 있다.
단점
예시 (as-is)
public class GameService {
public void startGame() {
System.out.println("게임을 시작합니다.");
Thread.sleep(1000L);
}
}
public class Client {
public static void main(String[] args) {
GameService gameService = new GameService();
gameService.startGame();
}
}
예시 (to-be) : 기존 코드를 건드릴 수 없을 때
public class GameService {
public void startGame() {
System.out.println("게임을 시작합니다.");
Thread.sleep(1000L);
}
}
public class GameServiceProxy extends GameService {
@Override
public void startGame() {
long before = System.currentTimeMillis();
super.startGame();
System.out.println(System.currentTimeMillis() - before);
}
}
public class Client {
public static void main(String[] args) {
GameService gameService = new GameServiceProxy();
gameService.startGame();
}
}
예시 (to-be) : 기존 코드를 건드릴 수 있을 때
public interface GameService {
void startGame();
}
public class DefaultGameService implements GameService {
@Override
public void startGame() {
System.out.println("게임을 시작합니다.");
Thread.sleep(1000L);
}
}
public class GameServiceProxy implements GameService {
private GameService gameService;
public GameServiceProxy(GameService gameService) {
this.gameService = gameService;
}
@Override
public void startGame() {
long before = System.currentTimeMillis();
// 굳이 주입하지 않게 기본 값을 넣는다.
if (this.gameService == null) {
this.gameService = new DefaultGameService();
}
gameService.startGame();
System.out.println(System.currentTimeMillis() - before);
}
}
public class Client {
public static void main(String[] args) {
GameService gameService = new GameServiceProxy(new DefaultGameService());
gameService.startGame();
}
}
활용
- 자바의 다이나믹 프록시, java.lang.reflect.Proxy : 런타임에 프록시 객체를 만들어줌
- Spring의 AOP : 여러 군데에 흩어진 코드를 Aspect라는 관점에 모아서 코딩할 수 있게 도와주는 기법
- interface의 경우 : 다이나믹 프록시를 사용해서 interface를 구현한 프록시 객체를 사용하게 된다.
- class의 경우 : CGLib을 사용해서 바이트 코드를 조작하여 만들어진 프록시 객체를 사용하게 된다.
- Spring의 @Transactional, @Cachable, ...