Proxy Pattern

JavaScript κΈ°μ€€μœΌλ‘œ μ„€λͺ…ν•©λ‹ˆλ‹€.

μ˜λ―Έμ™€ νŠΉμ„±

μ„€μ •, 쑰회 λ“±μ˜ 기본적인 객체 λ™μž‘μ„ μž¬μ •μ˜ν•˜λŠ” Proxy 객체λ₯Ό ν™œμš©ν•œ νŒ¨ν„΄μ΄λ‹€. μ–΄λ–€ 객체λ₯Ό μ‚¬μš©ν•˜κ³ μž ν•  λ•Œ, 객체λ₯Ό μ§μ ‘μ μœΌλ‘œ μ°Έμ‘°ν•˜λŠ” 것이 μ•„λ‹ˆλΌ Proxy 객체λ₯Ό 톡해 ν•΄λ‹Ή 객체에 μ ‘κ·Όν•œλ‹€.

ν”„λ‘μ‹œ νŒ¨ν„΄μ€ μ•„λž˜μ™€ 같은 νŠΉμ„±μ„ κ°–λŠ”λ‹€.

  • 원본 객체에 λŒ€ν•œ 접근을 μ œμ–΄ν•œλ‹€.

  • JavaScriptμ—μ„œλŠ” ν›„μˆ ν•  Proxy 객체λ₯Ό 톡해 주둜 κ΅¬ν˜„ν•œλ‹€.

JavaScript Proxy

νŠΉμ • 객체λ₯Ό 감싸 객체에 κ°€ν•΄μ§€λŠ” μž‘μ—…μ„ μ€‘κ°„μ—μ„œ κ°€λ‘œμ±„λŠ” λ‚΄μž₯ 객체이닀. new Proxy(target, handler); ν˜•νƒœλ‘œ μƒμ„±ν•œλ‹€. μ—¬κΈ°μ„œ target은 원본 객체이고, handlerλŠ” ν”„λ‘μ‹œμ˜ λ™μž‘μ„ μ •μ˜ν•˜λŠ” 객체이닀. 빈 handler둜 μƒμ„±λœ ν”„λ‘μ‹œ κ°μ²΄λŠ” 원본 객체와 λ™μΌν•œ λ™μž‘μ„ ν•˜λ©°, get, set λ“±μ˜ λ©”μ†Œλ“œλ₯Ό handler에 μΆ”κ°€ν•˜μ—¬ 원본 객체의 속성에 μ ‘κ·Όν•˜κ±°λ‚˜ μˆ˜μ •ν•  수 μžˆλ‹€. handlerλŠ” 원본 객체에 λŒ€ν•œ ν˜ΈμΆœμ„ μž‘μ•„λ‚Έλ‹€λŠ” μ˜λ―Έμ—μ„œ 트랩이라고 λΆˆλ¦¬κΈ°λ„ ν•œλ‹€.

생성 μ‹œ μ£Όμ˜ν•  점이 λͺ‡ κ°€μ§€ μžˆλ‹€.

Proxyλ₯Ό 더 κ°„λ‹¨ν•˜κ²Œ 생성할 수 있게 ν•˜λŠ” λ‚΄μž₯ 객체이닀. Proxy와 달리 ν•¨μˆ˜ 객체가 μ•„λ‹ˆλ―€λ‘œ μƒμ„±μžλ‘œ μ‚¬μš©λ˜μ§€ μ•ŠλŠ”λ‹€. (new μ—°μ‚°μžλ₯Ό 톡해 μƒμ„±ν•˜μ§€ μ•ŠλŠ”λ‹€.) Proxy handler의 get(), set() λ“±μ˜ κΈ°λ³Έ λ©”μ†Œλ“œλ₯Ό κ°–κ³  μžˆμœΌλ‚˜, ProxyλŠ” λ©”μ†Œλ“œλ₯Ό 직접 ν˜ΈμΆœν•  수 μ—†λŠ” 것에 λ°˜ν•΄ ReflectλŠ” κ°€λŠ₯ν•˜λ‹€.

Proxy handler λ‚΄λΆ€ λ™μž‘λ“€μ„ λŒ€μ²΄ν•˜λŠ” μ˜ˆμ‹œλ₯Ό μ‚΄νŽ΄λ³΄μž.

  • get() λ©”μ†Œλ“œ: obj[prop] -> Reflect.get(obj, prop)

  • set() λ©”μ†Œλ“œ: obj[prop] = value -> Reflect.set(obj, prop, value) 더 λ§Žμ€ μ˜ˆμ‹œλŠ” 이 λ¬Έμ„œμ—μ„œ 확인할 수 μžˆλ‹€.

κ΅¬ν˜„ μ˜ˆμ‹œ

ν”„λ‘μ‹œ νŒ¨ν„΄μœΌλ‘œ μœ νš¨μ„± 검사 κ΅¬ν˜„ν•˜κΈ°

const profile = {
  displayName: "Zero",
  age: 20,
};

const validateProfile = (prop, value) => {
  if (prop === "displayName") {
    if (value.length < 2) {
      throw new Error(`이름을 2자 이상 μž…λ ₯ν•΄μ£Όμ„Έμš”.`);
    }
  } else if (prop === "age") {
    if (value < 5) {
      throw new Error(`5μ„Έ μ΄μƒλ§Œ κ°€μž…ν•  수 μžˆμ–΄μš”.`);
    }
  }

  return true;
};

const profileProxy = new Proxy(profile, {
  get: (object, prop) => {
    if (!object[prop]) {
      throw new Error(`μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” μ†μ„±μ΄μ—μš”.`);
    } else {
      console.log(`${prop}: ${object[prop]}`);
    }
  },
  set: (object, prop, value) => {
    if (!object[prop]) {
      throw new Error(`μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” μ†μ„±μ΄μ—μš”.`);
    }

    if (validateProfile(prop, value)) {
      object[prop] = value;
      console.log(`${prop} 속성이 ${value}둜 λ³€κ²½λ˜μ—ˆμ–΄μš”.`);

      return true;
    }
  },
});

// throw μ‹œ 이후 μ½”λ“œλŠ” μ‹€ν–‰λ˜μ§€ μ•Šμ§€λ§Œ, 가독성을 μœ„ν•΄ λ‹¨μˆœ λ‚˜μ—΄ν–ˆμŠ΅λ‹ˆλ‹€.

// 쑰회
profileProxy.description; // β†’ Error: μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” μ†μ„±μ΄μ—μš”.
profileProxy.displayName; // β†’ displayName: Zero

// μˆ˜μ •
profileProxy.displayName = "!"; // β†’ Error: 이름을 2자 이상 μž…λ ₯ν•΄μ£Όμ„Έμš”.
profileProxy.displayName = "제둜"; // β†’ displayName 속성이 제둜둜 λ³€κ²½λ˜μ—ˆμ–΄μš”.

profileProxy.age = 3; // β†’ Error: 5μ„Έ μ΄μƒλ§Œ κ°€μž…ν•  수 μžˆμ–΄μš”.
profileProxy.age = 21; // β†’ age 속성이 21둜 λ³€κ²½λ˜μ—ˆμ–΄μš”.

Reflectλ₯Ό ν™œμš©ν•΄ κ΅¬ν˜„ν•˜κΈ°

// ...

const profileProxy = new Proxy(profile, {
  get: (object, prop) => {
    if (!Reflect.get(object, prop)) {
      throw new Error(`μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” μ†μ„±μ΄μ—μš”.`);
    } else {
      console.log(`${prop}: ${Reflect.get(object, prop)}`);
    }
  },
  set: (object, prop, value) => {
    if (!Reflect.get(object, prop)) {
      throw new Error(`μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” μ†μ„±μ΄μ—μš”.`);
    }

    if (validateProfile(prop, value)) {
      Reflect.set(object, prop, value);
      console.log(`${prop} 속성이 ${value}둜 λ³€κ²½λ˜μ—ˆμ–΄μš”.`);

      return true;
    }
  },
});

// 쑰회
profileProxy.displayName; // β†’ displayName: Zero

// μˆ˜μ •
profileProxy.displayName = "제둜"; // β†’ displayName 속성이 제둜둜 λ³€κ²½λ˜μ—ˆμ–΄μš”.
profileProxy.age = 21; // β†’ age 속성이 21둜 λ³€κ²½λ˜μ—ˆμ–΄μš”.

이 λ¬Έμ„œμ—μ„œ ν”„λ‘μ‹œλ₯Ό ν™œμš©ν•΄ κ΅¬ν˜„λœ λ‹€μ–‘ν•œ λͺ¨λ“ˆλ“€μ„ 확인할 수 μžˆλ‹€. 개인적으둜 python-rangeκ°€ κ°€μž₯ ν₯λ―Έλ‘œμ› λ‹€.

μž₯점

  • 원본 객체에 λŒ€ν•œ 접근을 μ œμ–΄ν•˜λŠ” νŠΉμ„±μƒ 가상 ν”„λ‘μ‹œ(ex. 이미지 μ§€μ—° λ‘œλ“œ), 보호 ν”„λ‘μ‹œ(ex. νŠΉμ • 객체에 λŒ€ν•œ μ ‘κ·Ό μ œν•œ) λ“±μ˜ λ‹€μ–‘ν•œ κ΅¬ν˜„ 상황에 μ μš©ν•  수 μžˆλ‹€.

단점

  • 객체 생성 μ‹œ ν•œ 단계λ₯Ό 더 거치게 λ˜λ―€λ‘œ, λΉˆλ²ˆν•˜κ²Œ μƒμ„±λ˜μ–΄μ•Ό ν•˜λŠ” 객체에 μ‚¬μš©λ˜μ—ˆμ„ λ•Œ 원본 객체λ₯Ό 직접 μ‚¬μš©ν•  λ•Œλ³΄λ‹€ μ„±λŠ₯이 μ €ν•˜λœλ‹€.

  • ν”„λ‘μ‹œ 객체의 λ©”μ„œλ“œ λ‚΄μ—μ„œ λ³΅μž‘ν•œ μž‘μ—…μ΄ λ°œμƒν•˜λ©΄ μ„±λŠ₯이 μ €ν•˜λ  수 μžˆλ‹€. (μ°Έκ³  λ¬Έμ„œ)

참고 자료

Last updated