Personal-Study/Design Patterns

[TS Design Patterns] 구조 패턴 - 프록시

Aaron-Kim 2023. 10. 26. 20:18

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";

 

- 활용


[아티클]

[TS 사용 예시]

[UI Component] Design Patterns - Proxy

반응형