Search
🎨

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

다른 객체를 감싸서 접근을 제어할 때 사용

1. 의도

GoF
다른 객체에 대한 접근을 제어하기 위한 대리자 또는 자리채움자 역할을 하는 객체를 둡니다.
Head First
특정 객체로의 접근을 제어하는 대리인(특정 객체를 대변하는 객체)를 제공

2. 활용성

단순한 포인터보다는 조금 더 다방면에 활용할 수 있거나 정교한 객체 참조자가 필요할 때 적용할 수 있습니다.

2.1. 종류

Remote Proxy (원격 프록시)
서로 다른 주소 공간에 존재하는 객체를 가리키는 대표 객체로, 로컬 환경에 위치
원격 객체의 로컬 대변자 역할
원격 객체로의 접근 제어
Virtual Proxy (가상 프록시)
요청이 있을 때만 고비용 객체를 생성
생성하기 힘든 자원으로의 접근 제어
Protection Proxy (보호 프록시)
원래 객체에 대한 실제 접근을 제어함. 객체별로 접근 제어 권한이 다를때 유용하게 사용 가능
접근 권한이 필요한 자원으로의 접근 제어
Smart Reference
원시 포인터의 대체용 객체로, 실제 객체에 접근이 일어날때 추가적인 행동을 수행함
Smart Pointer
실제 객체에 대한 참조 횟수를 저장하다가 더는 참조가 없을 때 자동으로 객체 제거
맨 처음 참조되는 시점에 영속적 저장소의 객체를 메모리로 옮김
실제 객체에 접근하기 전에, 다른 객체가 그것을 변경하지 못하도록 실제 객체에 대한 잠금(lock)을 검

3. 구조

UML Class Diagram

4. 참여자

Proxy는 종류에 따라 다음을 수행
Remote Proxy
요청 메세지와 인자를 인코딩하여 이를 다른 주소 공간에 있는 실제 대상에게 전달
Virtual Proxy
실제 대상에 대한 추가적 정보를 보유하여 실제접근을 지연할 수 있도록 해야 함
Protection Proxy
요청한 대상이 실제 요청할 수 있는 권한이 있는지 확인
참여자
역할
예시
Proxy
1. 실제 참조할 대상에 대한 레퍼런스 관리 - RealSubject와 Subject 인터페이스가 동일시, 프록시는 Subject에 대한 레퍼런스를 가짐 2. Subject와 동일한 인터페이스를 제공하여 실제 대상을 대체할 수 있어야함 3. 실제 대상에 대한 접근을 제어하고 실제 대상의 생성과 삭제를 책임 RealSubject로의 접근을 제어하는 객체. RealSubject 객체로의 접근 제어
Subject
- RealSubject와 Proxy에 공통적인 인터페이스 정의 - RealSubject가 요청되는 곳에 Proxy를 사용할 수 있게 함
RealSubject
프록시가 대표하는 실제 객체 - 실제 작업을 대부분 처리하는 객체

5. 협력 방법

프록시 클래스는 자신이 받은 요청을 RealSubject 객체에 전달합니다.

6. 결과

간접화 통로
프록시 패턴은 어떤 객체에 접근할 때 추가적인 간접화 통로를 제공합니다.
간접화 통로의 용도
Remote Proxy
객체가 다른 주소 공간에 존재한다는 사실 은닉 가능
Virtual Proxy
요구에 따라 객체를 생성하는 등 처리 최적화 가능
Protection Proxy & Smart Reference
객체가 접근할 때마다 추가 관리를 책임짐
객체를 생성할 건지 삭제할 건지 관리
기록 시점 복사 (copy-on-write)
프록시 패턴이 사용자에게 숨길 수 있는 또다른 최적화
중량급 객체에 대한 복사 비용 현격하게 줄어줌
덩치가 크고 복잡한 객체를 복사할때 비용이 많이 듬. 사본이 변경되지 않고 원본이 똑같다면 비용이 발생 X
프록시를 활용해서 복사 절차를 미룸 → 사본이 수정될 때만 실제 복사 비용을 물게 만듬
원본의 참조 카운트 관리
복사 기능 가능하기 위한 선행 작업
프록시를 복사하는 연산은 이 원본에 대한 참조 카운트를 증가시키는 일만 진행
사용자가 원본을 수정하는 연산을 요청할 때 프록시가 실제로 복사를 진행하여 사본이 별도의 값을 가지게 함
원본에 대한 참조자 수 감소
해당 과정의 반복 속에 참조 카운트가 0이 되면 대상 삭제

7. 예시 코드

// Interface for the network API interface NetworkAPI { request(endpoint: string): Promise<any>; } // Implementation of the network API class RealNetworkAPI implements NetworkAPI { public async request(endpoint: string): Promise<any> { console.log(`Making a network request to ${endpoint}`); // Simulate network latency await new Promise(resolve => setTimeout(resolve, 1000)); return { data: `Response from ${endpoint}` }; } } // Proxy class class NetworkAPIProxy implements NetworkAPI { private realNetworkAPI: RealNetworkAPI; private cache: Map<string, any>; constructor() { this.realNetworkAPI = new RealNetworkAPI(); this.cache = new Map<string, any>(); } public async request(endpoint: string): Promise<any> { if (this.cache.has(endpoint)) { console.log(`Returning cached data for ${endpoint}`); return this.cache.get(endpoint); } else { const response = await this.realNetworkAPI.request(endpoint); console.log(`Adding data to cache for ${endpoint}`); this.cache.set(endpoint, response); return response; } } } // Client code async function fetchData(endpoint: string, api: NetworkAPI): Promise<void> { const response = await api.request(endpoint); console.log(response); } const networkAPI = new NetworkAPIProxy(); await fetchData("https://example.com/data", networkAPI); await fetchData("https://example.com/data", networkAPI); // Making a network request to https://example.com/data // Adding data to cache for https://example.com/data // { data: 'Response from https://example.com/data' } // Returning cached data for https://example.com/data // { data: 'Response from https://example.com/data' }
TypeScript
복사