[TS Design Patterns] 구조 패턴 - 프록시
Proxy
- 다른 객체에 대한 대체 또는 placeholder를 제공할 수 있음
- 원래 객체에 대한 접근을 제어하므로, 요청이 원래 객체에 전달되기 전 또는 후에 무언가를 수행할 수 있도록 함
- 원래 서비스 객체와 같은 인터페이스로 새 프록시 클래스를 생성하도록 제안
-> 프록시 객체를 원래 객체의 모든 클라이언트들에 전달하도록 앱을 업데이트 할 수 있음
- 프록시는 지연된 초기화 및 결과값 캐싱을 클라이언트와 실제 DB 객체가 알지 못하는 상태에서 처리 가능
- 서비스의 객체 수명 주기 관리 가능
- 클라이언트가 사용하는 서비스 객체를 대신하는 객체를 제공하는 구조 디자인 패턴
- 클라이언트 요청을 수신하고, 일부 작업(접근 제어, 캐싱 등)을 수행한 다음 요청을 서비스 객체에 전달함
- 서비스 응답이 느려질 수 있음
- 지연된 초기화 (가상 프록시)
- 앱이 시작될 때 객체를 생성하는 대신, 객체 초기화를 실제로 초기화가 필요한 시점까지 지연 가능
- 접근 제어 (보호 프록시)
- 클라이언트의 자격 증명이 어떤 정해진 기준과 일치하는 경우에만 서비스 객체에 요청 전달 가능
- 원격 서비스의 로컬 실행 (원격 프록시)
- 각 요청을 서비스에 전달하기 전에 로깅 가능
- 요청 결과들의 캐싱 (캐싱 프록시)
- 항상 같은 결과를 생성하는 반복 요청들에 대한 캐싱 구현 가능
- 스마트 레퍼런스
- 서비스 객체 또는 그 결과에 대한 참조를 얻은 클라이언트 추적 가능
- 활성 상태가 아닌 서비스 객체를 닫고 시스템 자원 확보 가능
- 대상 객체에 대하여 읽기 및 쓰기를 직접 제어함
- Proxy 객체는 어떤 객체의 값을 설정하거나 값을 조회할 때 등의 인터랙션을 직접 제어할 수 있음
- 유효성 검사, 포맷팅, 알림, 디버깅 등 구현할 때 유용함
- 데이터 안전하게 관리 가능
- 객체의 동작을 커스터마이징 할 수 있는 유용한 기능
- 핸들러 객체에서 너무 헤비하게 사용하면 앱의 성능에 부정적인 영향을 줄 수 있음
- Reflect 빌트인 객체
- Proxy와 함께 사용하면 대상 객체 쉽게 조작 가능
- Reflect.get(), Reflect.set()
- 예시
/**
* The Subject interface declares common operations for both RealSubject and the
* Proxy. As long as the client works with RealSubject using this interface,
* you'll be able to pass it a proxy instead of a real subject.
*/
interface Subject {
request(): void;
}
/**
* The RealSubject contains some core business logic. Usually, RealSubjects are
* capable of doing some useful work which may also be very slow or sensitive -
* e.g. correcting input data. A Proxy can solve these issues without any
* changes to the RealSubject's code.
*/
class RealSubject implements Subject {
public request(): void {
console.log('RealSubject: Handling request.');
}
}
/**
* The Proxy has an interface identical to the RealSubject.
*/
class CustomProxy implements Subject {
private realSubject: RealSubject;
/**
* The Proxy maintains a reference to an object of the RealSubject class. It
* can be either lazy-loaded or passed to the Proxy by the client.
*/
constructor(realSubject: RealSubject) {
this.realSubject = realSubject;
}
/**
* The most common applications of the Proxy pattern are lazy loading,
* caching, controlling the access, logging, etc. A Proxy can perform one of
* these things and then, depending on the result, pass the execution to the
* same method in a linked RealSubject object.
*/
public request(): void {
if (this.checkAccess()) {
this.realSubject.request();
this.logAccess();
}
}
private checkAccess(): boolean {
// Some real checks should go here.
console.log('Proxy: Checking access prior to firing a real request.');
return true;
}
private logAccess(): void {
console.log('Proxy: Logging the time of request.');
}
}
/**
* The client code is supposed to work with all objects (both subjects and
* proxies) via the Subject interface in order to support both real subjects and
* proxies. In real life, however, clients mostly work with their real subjects
* directly. In this case, to implement the pattern more easily, you can extend
* your proxy from the real subject's class.
*/
function clientCode(subject: Subject) {
// ...
subject.request();
// ...
}
console.log('Client: Executing the client code with a real subject:');
const realSubject = new RealSubject();
clientCode(realSubject);
console.log('');
console.log('Client: Executing the same client code with a proxy:');
const proxy = new CustomProxy(realSubject);
clientCode(proxy);
const person = {
name: "John Doe",
age: 42,
nationality: "American"
};
const personProxy = new Proxy(person, {
get: (obj, prop) => {
if (!obj[prop]) {
console.log(`Hmm.. this property doesn't seem to exist`);
} else {
console.log(`The value of ${prop} is ${obj[prop]}`);
}
},
set: (obj, prop, value) => {
if (prop === "age" && typeof value !== "number") {
console.log(`Sorry, you can only pass numeric values for age.`);
} else if (prop === "name" && value.length < 1) {
console.log(`You need to provide a valid name.`);
} else {
console.log(`Changed ${prop} from ${obj[prop]} to ${value}.`);
obj[prop] = value;
}
return true;
}
});
personProxy.nonExistentProperty;
personProxy.age = "44";
personProxy.name = "";
const person = {
name: "John Doe",
age: 42,
nationality: "American"
};
const personProxy = new Proxy(person, {
get: (obj, prop) => {
console.log(`The value of ${prop} is ${Reflect.get(obj, prop)}`);
},
set: (obj, prop, value) => {
console.log(`Changed ${prop} from ${obj[prop]} to ${value}`);
return Reflect.set(obj, prop, value);
}
});
personProxy.name;
personProxy.age = 43;
personProxy.name = "Jane Doe";
- 활용