(JS) 프록시 패턴

프록시 패턴: 드래프트 패턴 중 하나입니다.


(JS) 프록시 패턴 1

프록시는 객체를 관찰하고 객체에 대해 특정 작업(getter로 필드 값 가져오기, setter로 필드 값 설정 또는 구성으로 인스턴스 생성)이 수행될 때 설정된 핸들러(메소드)를 통해 실행되는 사양입니다.

) . .

간단한 예로 Person이라는 개체가 있고 해당 개체를 프록시로 래핑합니다.

const person = {name:'Heather', age:29}

const proxyPerson = new Proxy(person, {
	// 여기서 원하는 행동을 선언해준다.

}

모니터링할 개체를 첫 번째 인수로 사용하고 처리기 개체를 두 번째 인수로 사용합니다.

내장 메소드는 핸들러 객체에 정의되어 있습니다.


(JS) 프록시 패턴 2

간단한 get과 set을 사용하자

const person = {name:'Heather', age:29}

const proxyPerson = new Proxy(person, {
	get:(target, property)=>{
    		console.log(`you're trying to get ${property}`)
        	return target(property)
        }
}

const output = proxyPerson.name // 'you're trying to get name'
console.log(output) // Heather

get 메소드를 사용하는 경우 getter를 통해 객체의 필드 값이 호출될 때마다 get 메소드 내의 함수가 호출되어 반환된 값이 getter의 값으로 반환됩니다.

const person = {name:'Heather', age:29}

const proxyPerson = new Proxy(person, {
	set:(target, property, value)=>{
    		console.log(`you're trying to set ${property}`)
        	target(property) = value;
            return true;
        }
}

proxyPerson.age = 30 // 'you're trying to set age'
console.log(proxyPerson.age) // 30

Set 메서드를 사용하면 setter가 개체의 필드 값을 설정할 때마다 Set 메서드의 함수가 호출됩니다.

set 메서드는 setter가 성공적으로 설정되었음을 나타내기 위해 true를 반환해야 합니다.

아… 개체가 특정 작업을 수행하고 필요한 기능을 호출하는 시기를 모니터링할 수 있습니다.

다음 클래스의 모든 메서드는 saveDate를 호출합니다.

그렇다면 클래스 개체를 프록시로 래핑하고 모니터링한 다음 메서드가 호출될 때 saveData를 호출하는 것이 좋지 않을까요?

대리수업은 고민에서 시작됐다.

import {saveData} from '../util/localstorage'

class RestaurantListHandler {
  private restaurants: Restaurant() = ();

  constructor() {
    this.restaurants = getSavedData(Constants.RESTAURANT_LIST);
  }

  getTotalRestaurants() {
    return this.restaurants;
  }

  addRestaurant(restaurant: Restaurant) {
    this.restaurants = (restaurant, ...this.restaurants);
    
    saveData('restaurantList',this.restaurants);
  }

  deleteRestaurant(id: string) {
    this.restaurants = this.restaurants.filter(
      (restaurant) => restaurant.id !
== id ); saveData('restaurantList',this.restaurants); } toggleBookmark(id: string) { this.restaurants = this.restaurants.map((restaurant) => { if (restaurant.id === id) { return { ...restaurant, bookmarked: !
restaurant.bookmarked }; } return restaurant; }); saveData('restaurantList',this.restaurants); } }

물론 saveData는 getRestaurant 메서드에서 호출되지 않지만 더 많은 작업을 수행할 수 있습니다.

프록시를 구현하자

const proxyRestaurantHandler = new Proxy(RestaurnatListHandler, {
	apply:(target, thisArg, Args)=>{
    	RestaurnatListHanlder(target)(Args);
        saveData('restaurantList', thisArg.restaurants);
    	}
}

proxyRestaurantHandler.deleteRestaurant('id123');

apply는 함수가 호출될 때마다 실행되는 메소드인데 이렇게 구현해도 될까요?

흠? 안되는데 뭐가 문제인지…

적용 메소드가 새로운 프록시의 첫 번째 인자로 함수를 입력했을 때와 그 함수를 호출했을 때 사용할 수 있는 메소드라는 것을 알게 되었습니다…다시…

첫 번째 인수로 객체(메서드 및 필드 포함)를 사용하고 해당 객체의 메서드가 호출될 때마다 프록시 적용 메서드를 호출할 수는 없습니까?

존재하지 않습니다.

.

이를 수행하는 한 가지 방법은 각 메소드를 프록시하고 내부에서 saveData를 호출하는 것입니다.

이 경우 왜 사용합니까? 그래서 안썼습니다.

덕분에 대리 공부를 할 수 있었습니다.

프록시는 모든 getter 및 setter 상황에서 유효성 검사와 같은 특정 동작이 필요할 때 유용합니다.

모든 getter 및 setter에서 필요하지 않은 경우 if 문으로 제어할 수 있지만 이러한 방식으로 프록시를 수행해야 하는 이유를 모르겠습니다.

그래도 기능이 단일 책임 원칙을 준수하도록 하는 좋은 방법인 것 같습니다.

마지막으로 적용을 사용하여 예제를 작성했습니다.

const sum = (a, b) => {
  return a + b;
}

const proxySum = new Proxy(sum, {
  apply: function(target, thisArg, argumentsList) {
    console.log(`arguments: ${argumentsList}`);
    const result = target.apply(thisArg, argumentsList);
    console.log(`returned: ${result}`);
    return result;
  }
});

proxySum(1, 2); // Logs "arguments: 1,2" and "returned: 3"