ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Proxy Pattern(프록시 패턴)
    STUDY/디자인패턴 2025. 1. 7. 20:23

    프록시 패턴이란?

    다른 객체에 대한 대체 또는 자리표시자를 제공할 수 있는 구조 디자인 패턴으로, 원래 객체에 대한 접근을 제어하므로, 클라이언트의 요청이 원래 객체에 전달되기 전 또는 후에 무언가를 수행할 수 있도록 한다.

     

    구조

     

    • ServiceInterface

    서비스와 프록시가 구현하는 인터페이스

     

    • Service

    어떤 유용한 비즈니스 로직을 제공하는 원본 대상 클래스

     

    • Proxy

    서비스 객체를 가리키는 참조 필드가 존재한다. 프록시가 요청의 처리(예: 초기화 지연, 로깅, 액세스 제어, 캐싱 등)를 완료하면, 그 후 처리된 요청을 서비스 객체에 전달한다. 일반적으로 프록시들은 서비스 객체들의 전체 수명 주기를 관리한다.

     

    • Client

    같은인터페이스를 통해 서비스들 및 프록시들과 함께 작동해야 한다. 그래야 서비스 객체를 기대하는 모든 코드에 프록시를 전달할 수 있다.

     

    예시

     

    Service Interface

    public interface ThirdPartyYouTubeLib {
        List<Video> listVideos();
        Video getVideoInfo(String id);
        File downloadVideo(String id);
    }

     

    Service

    public class ThirdPartyYouTubeClass implements ThirdPartyYouTubeLib {
        @Override
        public List<Video> listVideos() {
            //...
        }
    
        @Override
        public Video getVideoInfo(String id) {
            //...
        }
    
        @Override
        public File downloadVideo(String id) {
            //...
        }
    }

     

    Proxy

    public class YouTubeCacheProxy implements ThirdPartyYouTubeLib {
        private ThirdPartyYouTubeLib service;
        private List<Video> cacheList;
        private HashMap<String, Video> cacheVideo = new HashMap<String, Video>();
        private HashMap<String, File> cacheFile = new HashMap<String, File>();
    
        public YouTubeCacheProxy(ThirdPartyYouTubeLib service) {
            this.service = service;
        }
    
        @Override
        public List<Video> listVideos() {
            if (cacheList == null) {
                cacheList = service.listVideos();
            }
            
            return cacheList;
        }
    
        @Override
        public Video getVideoInfo(String id) {
            Video video = cacheVideo.get(id);
            if (video == null) {
                video = service.getVideoInfo(id);
                cacheVideo.put(id, video);
            }
            
            return video;
        }
    
        public File downloadVideo(String id) {
            File file = cacheFile.get(id);
            if (file == null) {
                file = service.downloadVideo(id);
                cacheFile.put(id, file);
            }
            
            return file;
        }
    }

     

    Client

    public class YouTubeManager {
        private ThirdPartyYouTubeLib service;
        
        public YouTubeManager(ThirdPartyYouTubeLib service) {
        	this.service = service;
        }
        
        public void renderVideoPage(String id) {
        	Video info = service.getVideoInfo(id);
            //...
        }
        
        public void renderListPanel() {
        	List<Video> list  = service.listVideos();
            // ...
        }
        
        public void reactOnUserInput() {
        	renderVideoPage();
            renderListPanel();
        }
    }
    
    
    public class Application {
        public static void main(String[] args) {
            ThirdPartyYouTubeLib proxy = new YouTubeCacheProxy(new ThirdPartyYouTubeClass());
            YouTubeManager manager = new YouTubeManager(proxy);
            manager.reactOnUserInput();
        }
    }

     

    적용

    1. 가상 프록시(지연 초기화). 앱이 시작될 때 객체를 생성하는 대신, 객체 초기화가 실제로 필요한 시점까지 지연할 수 있다. 가끔 필요한 무거운 서비스 객체가 항상 가동되어 있어 시스템 자원들을 낭비하는 경우에 사용
    2. 보호 프록시(접근 제어). 특정 클라이언트들만 서비스 객체를 사용할 수 있도록 하려는 경우에 사용
    3. 원격 프록시. 서비스 객체가 원격 서버에 존재하는 경우 사용하며, 프록시는 네트워크를 통해 클라이언트 요청을 전달하여 네트워크와 관련된 복잡한 작업들을 처리한다.
    4. 로깅 프록시. 서비스 객체에 대한 요청들의 기록을 유지하려는 경우에 사용하며, 각 요청을 서비스에 전달하기 전에 로깅할 수 있다.
    5. 캐싱 프록시. 클라이언트 요청들의 결과들을 캐시하고 이 캐시들의 수명 주기를 관리해야할 때, 특히 결과들이 상당히 큰 경우에 사용한다. 항상 같은 결과를 생성하는 반복 요청들에 대해 캐싱을 구현할 수 있다.
    6. 스마트 참조. 서비스 객체 또는 그 결과에 대한 참조를 얻은 클라이언트들을 추적할 수 있다. 클라이언트들을 점검하여 클라이언트들이 활성화 상태인지 확인할 수 있으며, 클라이언트 리스트가 비어 있으면 해당 서비스 객체를 닫고 시스템 자원을 확보한다.

     

    장단점

    장점

    • 클라이언트들이 알지 못하는 상태에서 서비스 객체를 제어할 수 있다.
    • 클라이언트들이 신경 쓰지 않을 때 서비스 객체의 수명 주기를 관리할 수 있다.
    • 프록시는 서비스 객체가 준비되지 않았거나 사용할 수 없는 경우에도 작동한다.
    • 개방/폐쇄 원칙 : 서비스나 클라이언트들을 변경하지 않고도 새 프록시들을 도입할 수 있다.

    단점

    • 새로운 클래스들을 많이 도입해야 하므로 코드가 복잡해질 수 있다.
    • 서비스의 응답이 늦어질 수 있다.

     

    참고

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

Designed by Tistory.