๐ ์๋ฐ์คํฌ๋ฆฝํธ Proxy & Reflect ๊ณ ๊ธ ๊ธฐ๋ฒ
JavaScript Proxy ๊ฐ์ฒด
ํ๋ก์(Proxy)์ ์ฌ์ ์ ๋ป์ '๋๋ฆฌ์ธ', '๋๋ฆฌ'๋ผ๋ ๋ป์ด๋ค. ์๋ฒ๋ฅผ ๋ค๋ค๋ณธ ๋ ์๋ถ๋ค์ด๋ผ๋ฉด ํ๋ก์ ์๋ฒ์ ๋ํด ์ง๋ฆฌ๋๋ก ๋ค์ด๋ดค์ ๊ฒ์ด๋ค. ํ๋ก์ ์๋ฒ๋ ํด๋ผ์ด์ธํธ์ ๋ณธ ์๋ฒ ์ค๊ฐ์ ์์นํ์ฌ ์บ์ฑ, ๋ถ์ฐ ๋ฑ ์ฌ๋ฌ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ํํ๋ ๋๋ฆฌ์ ์ญํ ๋ก์ ์ ๋ง ์ ์ฉํ๊ฒ ๋ค๋ค์ง๋ ๊ฐ๋ ์ด๋ค.
์ฌ์ง์ด ๋์์ธ ํจํด์์๋ ๋ฐ๋ก ํ๋ก์ ํจํด(Proxy Pattern) ์ผ๋ก ์ฝ๋ ํจํด์ ์ ์ํ์ฌ ์๊ฐํ๊ธฐ๋ ํ๋ค. ์ด ํ๋ก์ ์ฝ๋ ํจํด์ ์ค๋ฌด์์ ์ ๋ง ๋น๋ฒํ๊ฒ ๋ค๋ค์ง๊ธฐ ๋๋ฌธ์, ์ด์ ๊ฐ๊ฐ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์์๋ ๋ณ๋์ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด์ ์์ ๊ฐ๋ฐ์์๊ฒ API๋ก ์ ๊ณตํด์ค๋ค. ๋ํ์ ์ผ๋ก ์๋ฐ(Java) ์ง์์ Dynamic Proxy ๋ฅผ ๋ค ์ ์์ผ๋ฉฐ, ์๋ฐ์คํฌ๋ฆฝํธ(JavaScript) ์ง์์์๋ ES2015๋ถํฐ ๋ณ๋์ Proxy ๊ฐ์ฒด๋ฅผ ์ง์ํ๊ธฐ ์์ํ๋ค.
์๋ฐ์คํฌ๋ฆฝํธ์์์ Proxy ๊ฐ์ฒด์ ์ญํ ์ ๋์ ๊ฐ์ฒด์ ๊ฐ์ธ์(wrapping), ์์ฑ ์กฐํ, ํ ๋น, ์ด๊ฑฐ ๋ฐ ํจ์ ํธ์ถ ๋ฑ ์ฌ๋ฌ ๊ธฐ๋ณธ ๋์์ ๊ฐ๋ก์ฑ(trap) ํน๋ณํ ๋ค๋ฅธ ๋์์ ๊ฐ๋ฏธ์ํค๋ ๋๋ฆฌ์ ์ญํ ์ ํ๋ค. ๋์ ๊ฐ์ฒด๋ Object, Array ๋ฑ ์๋ฐ์คํฌ๋ฆฝํธ์ ๋ชจ๋ ์๋ฃํ์ด ๋์์ด ๋ ์ ์๋ค.
์๋ฅผ๋ค์ด obj.name = "ํ๊ธธ๋" ๊ณผ ๊ฐ์ ๊ฐ์ฒด์ ํ๋กํผํฐ์ ๊ฐ์ set ํ๋ ์์
์ ์ค๊ฐ์ ๊ฐ๋ก์ฑ์ด ์ถ๊ฐ ๋ก์ง์ ๊ฐ๋ฏธํ ์ ์๋ค๋ ๋ง์ด๋ค. ๊ฐ๋ก์ฑ์ง ์์
์ Proxy ์์ฒด์์ ์ฒ๋ฆฌ๋๊ธฐ๋ ํ๊ณ , ์๋ ๊ฐ์ฒด๊ฐ ์ฒ๋ฆฌํ๋๋ก ๊ทธ๋๋ก ์ ๋ฌ๋๊ธฐ๋ ํ๋ค.
์ด์ฒ๋ผ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์ด์ฉํ๋ฉด ๊ธฐ์กด ๊ฐ์ฒด์ ์์ฑ์ด๋ ๋ฉ์๋๋ฅผ ๋ฎ์ด์ธ ์ ์๊ณ , ๊ฐ์ฒด์ ์์ฑ์ด๋ ๋ฉ์๋์ ์ ๊ทผํ๋ ํ์์ ๋ํด ์ฝ๋ฐฑ์ ๋จน์ฌ, ๋ฐ์ดํฐ ๊ฒ์ฆ์ ์ฌ์ฉํ๋ Validator๋ก ์ฌ์ฉํ ์๋ ์๊ณ , ๋ฐ์ดํฐ ๋ณํ๋ฅผ ๊ฐ์งํ๋ ๋ก๊ฑฐ(logger)๋ก๋ ์ฌ์ฉํ ์ ์๋ค.
Proxy ๊ฐ์ฒด ๋ฑ๋กํ๊ธฐ
// ๋์ ๊ฐ์ฒด(Real Subject)
let target = { ... }
// target์ ๋์์ ๊ฐ๋ก์ฑ์ด ์ ์ดํ๋ ๋ฏธ๋ฆฌ ์ ์๋ ๋ฉ์๋ ๋ด๋ถ๋ฅผ ๊ตฌํํ ํธ๋ค๋ฌ
let handler = {
get(target, prop) { ... },
set(target, prop, value) { ... },
has(target, prop) { ... },
...
}
// ํ๋ก์ ์์ฑ ๋ฐ ๋ฑ๋ก
const proxy = new Proxy(target, handler)
target: Proxy์ ๋์์ด ๋๋ ๋์ ๊ฐ์ฒดhandler: target์ ๋์์ ์ ์ดํ๋ ๋ฉ์๋๋ฅผ ์ ์ํ ๊ฐ์ฒด
handler ๊ฐ์ฒด๋ฅผ ๋ณด๋ฉด, get, set, has ..๋ฑ ๋ฉ์๋๋ฅผ ์ ์ํ ๊ฒ์ ๋ณผ ์ ์๋๋ฐ, ์ด ๋ฉ์๋๋ค์ target ๊ฐ์ฒด์ ๋์์ ์ค๊ฐ์ ๊ฐ๋ก์ฑ์ ์ ์ด ๋ก์ง์ ๊ฐ๋ฏธํ๊ฒ ํ๋ ๋ฏธ๋ฆฌ ์ ์๋ ๋ฉ์๋๋ค์ด๋ค. ๊ทธ๋์ ์ด ๋ฉ์๋๋ค์ ํธ๋ฉ(trap) ์ด๋ผ๊ณ ๋ถ๋ฅธ๋ค.
๊ฐ๋จํ๊ฒ ํ๋ก์ ์์ ๋ฅผ ๋ค์ด๋ณด์๋ฉด ๋ค์๊ณผ ๊ฐ๋ค. ๋จผ์ ํ๊ฒ์ด ๋๋ user ๊ฐ์ฒด์ handler๋ฅผ ์ ์ํ๊ณ new Proxy()๋ฅผ ํตํด ํ๋ก์ ๊ฐ์ฒด๋ฅผ ๋ฑ๋กํด์ค๋ค. ๊ทธ๋ฆฌ๊ณ ๋์ ๊ฐ์ฒด์ firstName ๊ณผ lastName ์ ์ ๊ทผํด๋ณธ๋ค.
// ๋์ ๊ฐ์ฒด (target)
const user = {
firstName: 'John',
lastName: 'Doe',
email: 'john.doe@example.com',
}
// ํธ๋ค๋ฌ (handler)
const handler = {
// ๋์ ๊ฐ์ฒด์ ํ๋กํผํฐ ๊ฐ์ ํ ๋นํ๋ ค๋ ๋์(get)์ ๊ฐ๋ก์ฑ์ด ์คํ
get(target, property) {
console.log(`Property ${property} has been read.`);
return target[property];
}
}
const proxyUser = new Proxy(user, handler);
console.log(proxyUser.firstName);
console.log(proxyUser.lastName);
์ด๋ ์ฃผ์ํ ์ ์ ํด๋ผ์ด์ธํธ๋ ํ๋ก์ ๊ฐ์ฒด์ธ proxyUser๋ฅผ ํตํด ์ ๊ทผํด์ผ ๋๋ค๋ ์ ์ด๋ค. ์๋ํ๋ฉด ์์ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด user ๊ฐ์ฒด๋ฅผ ํ๋ก์ ๊ฐ์ฒด๊ฐ ๊ฐ์ธ๊ณ ์๋ ํํ์ด๊ธฐ ๋๋ฌธ์ ์ธ๋ถ ํฌ์ฅ๊ณผ ๊ฐ์ ํ๋ก์๋ฅผ ํตํด ๋์ ๊ฐ์ฒด์ธ user์ ํต์ ํ๊ฒ ๋๊ธฐ ๋๋ฌธ์ด๋ค. ์ฝ์ ๋ก๊ทธ๋ฅผ ์คํํ๋ฉด ์๋๋ ๊ฐ๋ง ๋ธ๋ ๋์์ผ ๋๋๊ฒ์ด ๋ค์๊ณผ ๊ฐ์ด ์ถ๊ฐ์ ์ผ๋ก ์ฝ๋๊ฐ ๊ฐ๋ฏธ๋จ์ ๋ณผ ์ ์๋ค.
์ด์ฒ๋ผ Proxy ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ฉด ๊ธฐ๋ณธ ์ธ์ด ์์ (์์ฑ ์กฐํ, ํ ๋น, ์ด๊ฑฐ, ํจ์ ํธ์ถ ..๋ฑ)์ ๋ํ ์ฌ์ฉ์ ์ง์ ๋์์ ๊ฐ๋ก์ฑ๊ณ ์ถ๊ฐ ์ฌ์ ์ํ ์ ์๋ค.
Proxy ํธ๋ฉ ํธ๋ค๋ฌ ์ข ๋ฅ
handler๋ก ๊ฐ๋ก์ฑ ์ ์๋ ๋์์ Get ํ์ ๋ฟ๋ง ์๋๋ผ ๊ฐ์ฒด์ ์์ฑ๊ณผ ํจ์ ๊ทธ๋ฆฌ๊ณ ํ๋กํ ํ์ ์ ์กฐ์ํ ์ ์๋ค.
Property ๊ฐ๋ก์ฑ๋ ํธ๋ฉ | ์๋ ์์ |
get | ํ๋กํผํฐ๋ฅผ ์ฝ์ ๋ |
set | ํ๋กํฐํฐ์ ๊ฐ์ ์ธ ๋ |
has | in ์ฐ์ฐ์๊ฐ ์๋ํ ๋ |
deleteProperty | delete ์ฐ์ฐ์๊ฐ ์๋ํ ๋ |
Method ๊ฐ๋ก์ฑ๋ ํธ๋ฉ | ์๋ ์์ |
apply | ํจ์๋ฅผ ํธ์ถํ ๋ |
constructor | new ์ฐ์ฐ์๊ฐ ์๋ํ ๋ |
Object ๊ฐ๋ก์ฑ๋ ํธ๋ฉ | ์๋ ์์ |
getPrototypeOf | Object.getPrototypeOf ์๋ํ ๋ |
setPrototypeOf | Object.setPrototypeOf ์๋ํ ๋ |
isExtensible | Object.isExtensible ์๋ํ ๋ |
preventExtensions | Object.preventExtensions ์๋ํ ๋ |
getOwnPropertyDescriptor | Object.getOwnPropertyDescriptor ์๋ํ ๋ |
ownKeys | Object.getOwnPropertyNames ์๋ํ ๋ Object.getOwnPropertySymbols ์๋ํ ๋ |
get ํธ๋ฉ
- ๋์ ๊ฐ์ฒด์ ํ๋กํผํฐ๋ฅผ ์ฝ์ ๋ ๋ฐ๋
let obj = {
name : "ํ๊ธธ๋"
};
let handler = {
get(target, prop, receiver) {
console.log("ํ๋กํผํฐ ์ฝ์ ๋ ์ค๊ฐ์ ๊ฐ๋ก์ฑ์ด ๋ก์ง ์ํ");
console.log('target: ', target); // ๋์ ๊ฐ์ฒด
console.log('prop: ', prop); // ํ๋กํผํฐ
console.log('receiver: ', receiver); // ํ๋ก์ ๊ฐ์ฒด this
return "์๊บฝ์ "; // ํ๋ก์์์ ์ด๋ฆ์ ๋ฐ๊ฟ๋ฒ๋ฆผ
},
};
const proxy = new Proxy(obj, handler);
console.log(proxy.name);
set ํธ๋ฉ
- ๋์ ๊ฐ์ฒด์ ํ๋กํผํฐ๋ฅผ ์ธ ๋ ๋ฐ๋
- ๊ฐ์ ์ฐ๋ ๊ฒ ์ฑ๊ณต์ ์ผ๋ก ์ฒ๋ฆฌ๋์์ผ๋ฉด ๋ฐ๋์ true๋ฅผ ๋ฐํํด์ผ ํ๋ค. ๊ทธ๋ ์ง ์์ ๊ฒฝ์ฐ๋ false๋ฅผ ๋ฐํํด์ผ ํ๋ค.
let obj = {
name : "ํ๊ธธ๋"
};
let handler = {
set(target, prop, value, receiver) {
console.log("ํ๋กํผํฐ ์ธ ๋ ์ค๊ฐ์ ๊ฐ๋ก์ฑ์ด ๋ก์ง ์ํ");
console.log('target: ', target); // ๋์ ๊ฐ์ฒด
console.log('prop: ', prop); // ํ๋กํผํฐ ์ด๋ฆ
console.log('value: ', value); // ํ ๋นํ ๊ฐ
console.log('receiver: ', receiver); // ํ๋ก์ ๊ฐ์ฒด this
target[prop] = value * 2;
return true;
},
};
const proxy = new Proxy(obj, handler);
proxy.age = 50;
console.log(proxy.age); // 100
console.log(obj.age); // 100
has ํธ๋ฉ
- ๋์ ๊ฐ์ฒด์ ํ๋กํผํฐ๊ฐ ์๋์ง ๊ฒ์ฌํ๋
in์ฐ์ฐ์๊ฐ ์๋ํ ๋ ๋ฐ๋
let obj = {
name : "ํ๊ธธ๋"
};
let handler = {
has(target, prop) {
console.log("ํ๋กํผํฐ ํ์ธ ๋ ์ค๊ฐ์ ๊ฐ๋ก์ฑ์ด ๋ก์ง ์ํ");
console.log('target: ', target); // ๋์ ๊ฐ์ฒด
console.log('prop: ', prop); // ํ๋กํผํฐ ์ด๋ฆ
},
};
const proxy = new Proxy(obj, handler);
if("name" in proxy) {
}
deleteProperty ํธ๋ฉ
- ๋์ ๊ฐ์ฒด์ ํ๋กํผํฐ๋ฅผ ์ญ์ ํ ๋ ๋ฐ๋
- ๊ฐ์ ์ง์ฐ๋ ๊ฒ ์ฑ๊ณต์ ์ผ๋ก ์ฒ๋ฆฌ๋์์ผ๋ฉด ๋ฐ๋์ true๋ฅผ ๋ฐํํด์ผ ํ๋ค. ๊ทธ๋ ์ง ์์ ๊ฒฝ์ฐ๋ false๋ฅผ ๋ฐํํด์ผ ํ๋ค.
let obj = {
name : "ํ๊ธธ๋"
};
let handler = {
deleteProperty(target, prop) {
console.log("ํ๋กํผํฐ ์ญ์ ํ ๋ ์ค๊ฐ์ ๊ฐ๋ก์ฑ์ด ๋ก์ง ์ํ");
console.log('target: ', target); // ๋์ ๊ฐ์ฒด
console.log('prop: ', prop); // ํ๋กํผํฐ ์ด๋ฆ
console.log("์ญ์ ์ ํ");
return false; // ์ผ๋ถ๋ฌ false๋ฅผ ๋ฐํํ๊ฒ ํด์ ์ญ์ ๋ฅผ ์ ํ ์ํด
},
};
const proxy = new Proxy(obj, handler);
delete proxy.name;
console.log(proxy.name); // ์ญ์ ๊ฐ ๋์ง์๊ณ ๊ทธ๋๋ก ๋จ์
apply ํธ๋ฉ
- ํจ์๋ฅผ ํธ์ถํ ๋ ๋ฐ๋
let obj = {
name: 'ํ๊ธธ๋',
print: function () {
console.log(`My Name is ${this.name}`);
},
};
let handler = {
apply(target, thisArg, args) {
console.log('๋ฉ์๋ ์คํํ ๋ ์ค๊ฐ์ ๊ฐ๋ก์ฑ์ด ๋ก์ง ์ํ');
console.log('target: ', target); // ๋์ ํจ์
console.log('thisArg: ', thisArg); // this์ ๊ฐ
console.log('args: ', args); // ๋งค๊ฐ๋ณ์ ๋ชฉ๋ก (๋ฐฐ์ด)
console.log('์ด๋ฆ ๋ฐ๊ฟ ๋ฒ๋ฆฌ๊ธฐ ~');
thisArg.name = '์๊บฝ์ ';
Reflect.apply(target, thisArg, args); // ๋์ ์๋ณธ ํจ์ ์คํ
},
};
// print ํจ์๋ฅผ ํ๋ก์๋ก ๊ฐ์ธ๊ธฐ
obj.print = new Proxy(obj.print, handler);
obj.print();
Proxy ํด์ ํ๊ธฐ
๋ง์ผ ์ธ๋ชจ์์ด์ง ํ๋ก์ ๊ฐ์ฒด๋ฅผ ํด์ฒดํ์ฌ ๋ฉ๋ชจ๋ฆฌ ์ธก๋ฉด์์ ์ฌ์ ๊ณต๊ฐ์ ์ป๊ณ ์ถ๋ค๋ฉด ์ด๋ป๊ฒ ํ ๊น?
Proxy ๊ฐ์ฒด์ Target ์๋ณธ ๊ฐ์ฒด๋ฅผ null๋ก ํด๋ฒ๋ฆฌ๋ฉด ์๋์ผ๋ก ํด์ฒด๋ ๊ฒ ๊ฐ์ ๋ณด์ด์ง๋ง ๋ฐ์๋์ง ์๋๋ค. ๋ฐ๋ผ์ ์ง์ ๋ช ์์ ์ผ๋ก ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์ ๊ฑฐํ ํ์๊ฐ ์๋ค.
ํ๋ก์๋ฅผ ํด์งํ์ง ์ํด์ , ์ฒ์ ๋ถํฐ ์ทจ์ ๊ฐ๋ฅ ํ๋ก์(Revocable Proxy)๋ก ์ ์ํ ํ์๊ฐ ์๋๋ฐ, Proxy.revocable() ๋ฅผ ํตํด ์ป์ ์ ์๋ revoke ๋ฅผ ์ด์ฉํด์ Target ๊ฐ์ฒด ๋ฅผ ์ฐธ์กฐํ๋ Proxy ๊ฐ์ฒด๋ฅผ ํด์ ํ ์ ์๋ค.
let obj = {
name: 'ํ๊ธธ๋',
};
let handler = {
get(target, key) {},
set(target, key, value) {},
};
// ์ทจ์ ๊ฐ๋ฅํ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ๊ตฌ์กฐ๋ถํด๋ก ์ป๊ธฐ
const { proxy, revoke } = Proxy.revocable(obj, handler);
try {
revoke(); // ํ๋ก์ ํด์
proxy.name = "์๊บฝ์ "; // ! ํ๋ก์ ์ฌ์ฉ ๋ถ๊ฐ - Cannot perform 'set' on a proxy that has been revoked
} catch (err) {
console.error(err.message);
}
JavaScript Reflect ๊ฐ์ฒด
ํ๋ก๊ทธ๋๋ฐ์์ ๋ฆฌํ๋ ์ (Reflection)์ด๋ผ๋ ๊ฒ์ ๋ฐํ์ ๋จ์์ ๊ฒ์ฒด์ ๋ณ์, ์์ฑ ๋ฐ ๋ฉ์๋๋ฅผ ๋ค์ด๋๋ฏนํ๊ฒ ์กฐ์ํ๋ ํ๋ก์ธ์ค๋ฅผ ๋งํ๋ค.
์ฌ์ค ์๋ฐ์คํฌ๋ฆฝํธ์์ ์ด๋ฏธ ๋ฆฌํ๋ ์
๊ธฐ๋ฅ์ด ์์๋ค. ์๋ฅผ ๋ค์ด Object.keys(), Object.getOwnPropertyDescriptor() ๋ฐ prototype์ ๊ฐ์ Object ๋ฉ์๋๋ค์ ์ด์ฐ๋ณด๋ฉด ๊ณ ์ ์ ์ธ ๋ฆฌํ๋ ์
๊ธฐ๋ฅ์ด๋ผ๊ณ ๋งํ ์ ์๋ค. ํ์ง๋ง ES6์์๋ ์์ ๊ฐ์ ์ฌ์ฉ์ฑ์ด ๋์๊ณ ๋ฉ์๋๋ช
์ด ๊ธธ์ด, ์์ Refelct ๋ผ๋ ์๋ก์ด ์ ์ญ ๊ฐ์ฒด๋ฅผ ์ถ๊ฐํ์๋ค.
์ฆ, Reflect ๊ฐ์ฒด๋ Proxy์ ๊ฐ์ด ๋ช ๋ น์ ๊ฐ๋ก์ฑ ์ ์๋ ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ ๋ด์ฅ ๊ฐ์ฒด๋ก์ ์ข๋ ์ฌํํํ ๊ฐ์ฒด๋ผ๊ณ ๋ณด๋ฉด ๋๋ค.
Reflect ๋ฉ์๋ ์ข ๋ฅ
Reflect ๊ฐ์ฒด๋ ์์ฑ์๊ฐ ์กด์ฌํ์ง ์์ new ์ธ์คํด์คํ๋ฅผ ํตํด ์ฌ์ฉํ๋ ๊ฒ์ด ์๋, Math๋ JSON ๊ฐ์ฒด์ฒ๋ผ ์ ์ ๋ฉ์๋๋ง ์ง์ํ๋ค. ๊ทธ๋ฆฌ๊ณ Proxy์ ๋ชจ๋ ํธ๋ฉ ์ข ๋ฅ๋ฅผ ๋์ผํ ์ธํฐํ์ด์ค๋ก ์ง์ํ๋ค.
Reflect ํธ๋ค๋ฌ | ๋ด๋ถ ์๋ ๋ฐฉ์ |
Reflect.get() | ์์ฑ ๊ฐ์ ๋ฐํ obj[prop] ํธ์ถํ๋ ๊ฒ๊ณผ ๊ฐ์ |
Reflect.set() | ์์ฑ ๊ฐ์ ํ ๋นํ๋ฉด true ๋ฐํ obj[prop] = value ํธ์ถํ๋ ๊ฒ๊ณผ ๊ฐ์ |
Reflect.has() | ์์ฑ(์์ ๋๋ ์์)์ด ์กด์ฌํ๋์ง ์ฌ๋ถ๋ฅผ ๋ํ๋ด๋ ๋ถ์ธ์ ๋ฐํ |
Reflect.deleteProperty() | ์์ฑ ๊ฐ์ ์ญ์ delete obj[prop] ํธ์ถํ๋ ๊ฒ๊ณผ ๊ฐ์ |
Reflect.construct() | ์์ฑ์ ํธ์ถ |
Reflect.apply() | ์ง์ ๋ ์ธ์๋ก ํจ์๋ฅผ ํธ์ถ |
Reflect.defineProperty() | ์์ฑ์ด ๊ฐ์ฒด์ ์์ผ๋ฉด ์์ฑ์ ์์ฑ ์ค๋ช ์๋ฅผ ๋ฐํํ๊ณ ๊ทธ๋ ์ง ์์ผ๋ฉด ์ ์๋์ง ์์ ์์ฑ์ ๋ฐํ |
Reflect.getOwnPropertyDescriptor() | Object.getOwnPropertyDescriptor() ์ ๊ฐ์ |
Reflect.getPrototypeOf() | Object.getPrototypeOf() ์ ๊ฐ์ |
Reflect.isExtensible() | Object.isExtensible() ์ ๊ฐ์ |
Reflect.ownKeys() | ๊ฐ์ฒด์ ์์ ์์ฑ ํค(์์๋์ง ์์)์ ๋ฐฐ์ด์ ๋ฐํ |
Reflect.preventExtensions() | Object.preventExtensions() ์ ๊ฐ์ |
Reflect.setPrototypeOf() | ๊ฐ์ฒด์ ํ๋กํ ํ์ ์ ์ค์ |
Reflect.get
- ๊ฐ์ฒด์ ์์ฑ์ ์กฐํ
const obj = { a: 1, b: 'zero', c: true };
const arr = [1, 'zero', true];
Reflect.get(obj, 'a'); // 1
Reflect.get(arr, 1); // 'zero'
Reflect.set
- ๊ฐ์ฒด์ ์์ฑ์ ๊ฐ์ ์ค์
Reflect.set(obj, 'd', ['arg1', 'arg2']); // true ๋ฐํ
obj.d; // ['arg1', 'arg2']
Reflect.has
- ๊ฐ์ฒด๊ฐ ํด๋น ์์ฑ์ ๊ฐ์ก๋์ง ๊ฒ์ฌ
Reflect.has(obj, 'b'); // true
Reflect.apply
- ํจ์๋ฅผ ์คํ
- ๋ ๋ฒ์งธ ์ธ์๋ ํจ์์ this๋ฅผ ๋ฐ๊พธ๊ณ ์ถ์ ๋ ์ฌ์ฉํ๋ ๊ฑฐ๋ผ์, ๋ณดํต์ null์ ๋ฃ์ด์ค๋๋ค.
- ์ธ ๋ฒ์งธ ์ธ์๋ ํจ์์ ๋ฃ์ ์ธ์์ด๋ค.
- call, apply, bind ์ ์ฌ์ฉ๋ฒ์ด ๋น์ทํ๋ค๊ณ ๋ณด๋ฉด ๋๋ค.
const add = (a, b) => a + b;
Reflect.apply(add, null, [3, 5]); // 8
Reflect.construct
- ์์ฑ์
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
const p = Reflect.construct(Person, ['ํ๊ธธ๋', 55]); // new Person('ํ๊ธธ๋', 55)
Reflect.ownKeys
- ๊ฐ์ฒด์ ์์ฑ๋ช ๋ค์ ๋ฐฐ์ด๋ก ๋ฐํ
- ๋จ, ์์๋ฐ์ ์์ฑ๊ณผ enumerableํ์ง ์์ ๊ฒ์ ์ ์ธ
const duck = {
name: 'Maurice',
color: 'white',
greeting: function() {
console.log(`Quaaaack! My name is ${this.name}`);
}
}
Reflect.ownKeys(duck); // [ "name", "color", "greeting" ]
Proxy + Reflect ์กฐํฉํ๊ธฐ
์๋ฐ์คํฌ๋ฆฝํธ Proxy ๊ฐ์ฒด๋ฅผ ์ด์ฉํด [[GET]] ํธ๋ฉ ํธ๋ค๋ฌ๋ฅผ ์ค์ ํ์๋ค. ๋ง์ผ Target Object ์์ [[Get]] ์ฐ์ฐ์ด ๋ฐ์ํ๋ฉด, Proxy Handler ์ ์ ์ํ get ํจ์๊ฐ ๋จผ์ ํธ์ถํ๊ฒ ๋ ๊ฒ์ด๋ค. ๋ฐ๋ผ์ get ํธ๋ฉ ํจ์์ ์ ์ํ ๋ด์ฉ์ ๋ฐ๋ผ Target Object ์ [[Get]] ์ฐ์ฐ๊ณผ ๋ณ๊ฐ๋ก ๋์์ด ๊ฐ๋ฅํ๋ค.
๋ง์ฝ Target Object ์ [[Get]] ์ฐ์ฐ์ ์ด์ด๋๊ฐ๊ณ ์ถ์๋ ์ฐ๋ฆฌ๋ ๋งค๊ฐ๋ณ์๋ฅผ ์ด์ฉํด target[prop] = value ๋ก ์ฒ๋ฆฌ ํ์์๋ค. ํ์ง๋ง ์ผ๋ฐ์ ์ธ Proxy์ ํธ๋ฉ ํ๋ผ๋ฏธํฐ๋ฅผ ์ด์ฉํ ๋ฐฉ๋ฒ์ ํ์ฌ ์ผ์ด๋๋ ํ์์ ์ฃผ์ฒด๊ฐ ๋ฌด์์ธ์ง ์ ์๊ฐ ์๊ธฐ ๋๋ฌธ์ ์ฌ์ด๋ ์ดํํธ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๊ฒ ๋๋ค. ๊ทธ๋์ ์ค๋ฌด์์ ์๋์ ๊ฐ์ด Reflect ๋ฉ์๋๋ก ์กฐํฉํด์ ์ฌ์ฉํ๋ค.
Reflect๋ Proxy์์ ํธ๋ฉํ ์ ์๋ ๋ชจ๋ ๋ด๋ถ ๋ฉ์๋์ ๋์ผํ ๋ด์ฅ ๋ฉ์๋๋ฅผ ๊ณ ๋๋ก ๊ฐ์ง๊ณ ์๋ค. ๋ฐ๋ผ์ Reflect๋ฅผ ์ฌ์ฉํ๋ฉด ์๋ ๊ฐ์ฒด์ ๊ทธ๋๋ก ์์ ์ ์ ๋ฌํ ๋ ๋ณ๋์ ์ฌ์ด๋ ์ดํํธ ์์ด ์ ๋ฌ ๊ฐ๋ฅํ๋ค๋ ์ฅ์ ์ด ์๋ค.
Proxy / Reflect ์์ฉ ์์ ๋ชจ์
๊ฐ์ ํ ๋นํ ๋ ๋ฌธ์์ด ํํฐ๋ง ํ๊ธฐ
๋ง์ผ Person ๊ฐ์ฒด์ ์ด๋ฆ์ ์ง์ ํ๋๋ฐ ์์ด ์ซ์๊ฐ ๋ค์ด์ค๊ฑฐ๋ 10์๋ฆฌ ์ด์ ๊ธด ๋ฌธ์์ด์ด ๋ค์ด์ฌ๊ฒฝ์ฐ ์ด๋ฅผ ํํฐ๋งํ๊ณ ์ถ์๋ฐ ๋ถ๋์ดํ๊ฒ Person ๊ฐ์ฒด์ ๋ฉค๋ฒ๋ฅผ ์์ ํ ์ ์์ ๊ฒฝ์ฐ ํ๋ก์๋ฅผ ์ด์ฉํด ์ถ๊ฐ ๋ก์ง์ ๊ฐ๋ฏธ ์ํฌ ์ ์๋ค.
class Person {
name;
setName(name) {
this.name = name;
}
}
let me = new Person();
me.setName(123123); // ์๋ฌ๊ฐ ๋จ๊ฒ ํ๊ณ ์ถ๋ค.
me.setName('asdjhfkjasdhflkjshadfjhsadkfjl'); // ์๋ฌ๊ฐ ๋จ๊ฒ ํ๊ณ ์ถ๋ค.
setName() ์ด๋ผ๋ ๋ฉ์๋ ์คํ ๋ก์ง์ ๊ฐ๋ก์ฑ์ด ๊ฐ๋ฏธํด์ผ ๋๊ธฐ ๋๋ฌธ์ apply ํธ๋ฉ ํธ๋ค๋ฌ๋ฅผ ์ด์ฉํ์ฌ ๋ค์๊ณผ ๊ฐ์ด ๊ตฌํํ ์ ์๋ค.
class Person {
name;
setName(name) {
this.name = name;
}
}
let me = new Person();
me.setName = new Proxy(me.setName, {
apply(target, thisArg, args) {
const value = args[0];
if (typeof value !== 'string') {
throw new TypeError('์ด๋ฆ์ ๋ฌธ์์ด ๋ฐ์ดํฐ๋ง ๊ฐ๋ฅํฉ๋๋ค.');
} else if (value.length > 10) {
throw new RangeError('์ด๋ฆ์ ์ต๋ 10๊ธ์ ์ด๋ด ์ฌ์ผ ํฉ๋๋ค.');
}
return Reflect.apply(target, thisArg, args);
}
})
try {
me.setName(123123);
} catch (e) {
console.error(e.message);
}
try {
me.setName('asdjhfkjasdhflkjshadfjhsadkfjl');
} catch (e) {
console.error(e.message);
}
me.setName('inpa');
console.log(me.name);
์กด์ฌํ์ง ์์ ํ๋กํผํฐ๋ ์๋ฌ ๋ฐ์ ์ํค๊ธฐ
์๋ฐ์คํฌ๋ฆฝํธ๋ ์ ์ฐํ ์ธ์ด์ด๊ธฐ ๋๋ฌธ์ ๊ฐ์ฒด์ ๋ฑ๋ก๋์ง ์์ ํ๋กํผํฐ๋ผ๋ ์ ๊ทผํ๋ฉด ๊ทธ๋ฅ undefined๋ฅผ ๋ฐํํ๊ณ ๋ง๋ค. ํ์ง๋ง ์ด๋ ์คํ๋ ค ๊ฐ๋ฐ ์์ฐ์ฑ์ ์ค์ด๋ค๊ฒ ํ๋ ์์ธ์ด๋ค. ๋ฐ๋ผ์ ์ด๋ฅผ ํ์ดํธํ๊ฒ ๊ด๋ฆฌํ๊ธฐ ์ํ์ฌ ์กด์ฌํ์ง ์์ ํ๋กํผํฐ๋ฅผ ์ ๊ทผํ ๊ฒฝ์ฐ ์๋ฌ๋ฅผ ๋์ง๊ฒ ๊ตฌํํ๊ณ ์ถ๋ค.
let user = {
name: 'inpa',
};
console.log(user.name); // inpa
console.log(user.age); // undefined
get ํธ๋ฉ์์ ์ค๊ฐ์ ๊ฐ๋ก์ฑ์ด in ์ฐ์ฐ์๋ฅผ ์ด์ฉํด prop์ ๋ํ ์กด์ฌ ์ฌ๋ถ ๊ฒ์ฌ๋ฅผ ๋ถ๊ธฐํด์ฃผ๋ฉด ๋๋ค.
let user = {
name: 'inpa',
};
user = new Proxy(target, {
get(target, prop, receiver) {
if (prop in target) {
return Reflect.get(target, prop, receiver);
} else {
throw new Error(`ReferenceError: Property doesn't exist "${prop}"`);
}
},
});
console.log(user.name); // inpa
console.log(user.age); // ReferenceError: Property doesn't exist "age"
์์ ์ธ๋ฑ์ค ๊ตฌํํ๊ธฐ
์๋ฐ์คํฌ๋ฆฝํธ์ ๋ฐฐ์ด ์ธ๋ฑ์ค๋ ํ์ด์ฌ ๊ณผ๋ ๋ฌ๋ฆฌ ์์ ์ธ๋ฑ์ค๋ฅผ ์ง์ํ์ง ์๋๋ค. ํ์ง๋ง ์ด๋ฅผ ํ๋ก์๋ฅผ ์ด์ฉํด ๊ตฌํํ ์ ์๋ค.
let array = ["ํ๊ธธ๋", "์๊บฝ์ ", "๋ฐํ๊ฑฐ์ธ"];
// ๋ง์ง๋ง ์์ : ๋ฐํ๊ฑฐ์ธ
console.log('array[-1]: ', array[-1]);
// ๋ค์์ ๋ ๋ฒ์งธ ์์ : ์๊บฝ์
console.log('array[-2]: ', array[-2]);
// ๋ค์์ ์ธ ๋ฒ์งธ ์์ : ํ๊ธธ๋
console.log('array[-3]: ', array[-3]);
๊ฐ๋จํ๊ฒ ํ๋ก์ get ํธ๋ฉ์ prop ๋งค๊ฐ๋ณ์์ ๋ด๊ธฐ๋ ์ธ๋ฑ์ค ์ซ์๊ฐ 0๋ณด๋ค ์์ ๊ฒฝ์ฐ ๋ฐฐ์ด length์ ์ ์ ํ ๊ณ์ฐ์ผ๋ก ์ ์ ์ธ๋ฑ์ค๋ก ๋ณํํ๋ค ๊ทธ๋๋ก ์คํํด์ฃผ๋ฉด ๋๋ ์ผ์ด๋ค.
let array = ["ํ๊ธธ๋", "์๊บฝ์ ", "๋ฐํ๊ฑฐ์ธ"];
array = new Proxy(array, {
get(target, prop, receiver) {
let propNum = Number(prop); // ๋ฌธ์๋ฅผ ์ซ์๋ก ๋ณํ
if(propNum < 0) {
propNum += target.length;
}
return Reflect.get(target, propNum, receiver);
}
})
// ๋ง์ง๋ง ์์ : ๋ฐํ๊ฑฐ์ธ
console.log('array[-1]: ', array[-1]);
// ๋ค์์ ๋ ๋ฒ์งธ ์์ : ์๊บฝ์
console.log('array[-2]: ', array[-2]);
// ๋ค์์ ์ธ ๋ฒ์งธ ์์ : ํ๊ธธ๋
console.log('array[-3]: ', array[-3]);
# ์ฐธ๊ณ ์๋ฃ
https://www.javascripttutorial.net/es6/javascript-proxy/
https://www.zerocho.com/category/ECMAScript/post/57ce7fec2a00e600151f085c
https://brunch.co.kr/@skykamja24/644
https://ko.javascript.info/proxy