프록시(Proxy)

  • 프록시는 다른 객체에 대한 접근을 제어하는 객체이다
  • 다른 객체를 대상(subject)라고 한다.
  • 프록시와 대상은 동일한 인터페이스를 가지고 있으며 이를 통해 다른 인터페이스와 완전히 호환되도록 바꿀 수 있다.
  • 프록시는 대상에서 실행될 작업의 전부 또는 일부를 가로채서 해당 동작을 행상시키거나 보완한다.
  • 프록시는 각 작업을 대상으로 전달하여 추가적인 전처리 또는 후처리로 동작을 향상 시킨다.

image.png

프록시(Proxy)

프록시는 아래와 같은 형태로 실전에서 사용이 가능하다.

  • 데이터 유효성 검사
    • 프록시가 입력을 대상으로 전달하고 유효성을 검사한다.
  • 보안
    • 프록시는 클라이언트가 작업을 수행할 수 있는 권한이 있는지 확인하고 검사 결과가 긍정적인 경우에만 요청을 대상으로 전달
  • 캐싱
    • 프록시가 내부 캐시를 유지하여 데이터가 캐시에 아직 존재하지 않는 경우에만 대상에서 작업이 실행되도록 한다,
  • 지연 초기화
    • 대상의 생성 비용이 비싸다면 프록시는 그것을 필요로할 때까지 연기
  • 로깅
    • 프록시는 메소드 호출과 상대 매개 변수를 인터셉트하고 이를 기록한다.
  • 원격객체
    • 프록시는 원격 위치에 있는 객체를 가져와서 로컬처럼 보이게할 수 있다.

자바스크립트에서 프록시 패턴을 어떻게 구현하는지 알아보자. 먼저, prototype으로 객체를 정의 했다.


function StudentList() {
  this.students = {
    'park': 'a company',
    'kim': 'b company',
    'su': 'c company'
  };
}

StudentList.prototype.get = function(name) {
  const self = this;
  setTimeout(function() {
    console.log(self.students[name]);
  }, 3000);
}

StudentList라는 객체에 Proxy를 생성한 형태를 알아보자. studentListProxy라는 객체를 생성하고, 객체 내부에서는 studentList를 참조하고 있다.
proxy를 통해서 get 메서드를 실행해서 수강생을 조회하면 log를 남기도록 했다.

프록시는 대상에서 실행될 작업의 전부 또는 일부를 가로채서 해당 동작을 행상시키거나 보완한다.

StudentListPorxy라는 프록시를 통해서 대상(subject)를 참조해서 학생을 읽어오고, 동시에 로그도 남기도록 했다.

function StudentListProxy() {
  const studentList = new StudentList();

  return {
    get: function(name) {
      this.getLog()
      studentList.get(name);
    },

    getLog: function() {
      console.log("get student");
    }
  };
}

다른 예제를 통해서 프록시 패턴을 어떻게 적용하는지 알아보자. 프록시패턴을 통해서 캐싱기능을 제공하도록 구현하였다. GeoLocation이라는 객체를 정의하고, 도시 이름을 입력받으면 위도/경도를 return하는 메서드가 기본적으로 구현되어있다. Proxy는 GeoLocation을 참조해서, getLatLng라는 별도의 메서드를 생성하는데 프록시에서는 cache object에 해당 데이터가 존재하는지 확인하고 있으면 캐시에서 리턴하고 없으면 원본에서 읽어오도록 구현을 하였다.


function Geolocation () {
    this.getLatLng = function(address) {
        if(address === "Amsterdam") {
            return "52.3700° N, 4.8900° E";
        }else if (address === "London") {
            return "51.5171° N, 0.1062° W";
        } else if (address === "Paris") {
            return "48.8742° N, 2.3470° E";
        } else if (address === "Berlin") {
            return "52.5233° N, 13.4127° E";
        } else {
            return "";
        }
    }
}

function GeoProxy() {
    const geolocation = new Geolocation();
    let cache = {};

      return {

        getLatLng : function(address) {
            if(!cache[address]) {
                cache[address] = geolocation.getLatLng(address);
            }
              return cache[address];
        }
    }

}

ES6 Proxy

es6 사양에서 Proxy라는 전역 객체가 도입되었다. Proxy API에는 타겟 및 핸들러를 인자로 허용하는 Proxy 생성자가 포함되어 있다.
타겟은 프록시가 적용되는 객체(subject)를 나타내며, handler는 프록시의 동작을 정의하는 특수항 객체이다. 핸들러 객체에는 해당 작업이 프록시 인스턴스에게 수행될 때 자동으로 호출되는 트랩 메소드 (apply,get,set,has)라는 사전에 정의된 이름을 가진 선택적 메소드들이 포함되어 있다.

const proxy = new Proxy(target,handler);

아래의 예제는, 프록시 API를 사용하여 target객체인 scientist의 모든 속성에 대한 액세스를가로채서 원래의 속성값을 대문자로 변환한다.

const scientist = {
    name : "nikola",
    surname : "tesla"
}

const uppercaseScientist = new Proxy(scientist, {
    get : (target,property) => target[property].toUpperCase()
});

console.log(uppercaseScientist.name, uppercaseScientist.surname);