Language/JavaScript

๐Ÿ“š ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ Proxy & Reflect ๊ณ ๊ธ‰ ๊ธฐ๋ฒ•

์ธํŒŒ_ 2023. 1. 16. 08:41

javascript-Proxy-Reflect

JavaScript Proxy ๊ฐ์ฒด

ํ”„๋ก์‹œ(Proxy)์˜ ์‚ฌ์ „์  ๋œป์€ '๋Œ€๋ฆฌ์ธ', '๋Œ€๋ฆฌ'๋ผ๋Š” ๋œป์ด๋‹ค. ์„œ๋ฒ„๋ฅผ ๋‹ค๋ค„๋ณธ ๋…์ž๋ถ„๋“ค์ด๋ผ๋ฉด ํ”„๋ก์‹œ ์„œ๋ฒ„์— ๋Œ€ํ•ด ์งˆ๋ฆฌ๋„๋ก ๋“ค์–ด๋ดค์„ ๊ฒƒ์ด๋‹ค. ํ”„๋ก์‹œ ์„œ๋ฒ„๋Š” ํด๋ผ์ด์–ธํŠธ์™€ ๋ณธ ์„œ๋ฒ„ ์ค‘๊ฐ„์— ์œ„์น˜ํ•˜์—ฌ ์บ์‹ฑ, ๋ถ„์‚ฐ ๋“ฑ ์—ฌ๋Ÿฌ ๋ถ€๊ฐ€ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋Œ€๋ฆฌ์ž ์—ญํ• ๋กœ์„œ ์ •๋ง ์œ ์šฉํ•˜๊ฒŒ ๋‹ค๋ค„์ง€๋Š” ๊ฐœ๋…์ด๋‹ค.

์‹ฌ์ง€์–ด ๋””์ž์ธ ํŒจํ„ด์—์„œ๋Š” ๋”ฐ๋กœ ํ”„๋ก์‹œ ํŒจํ„ด(Proxy Pattern) ์œผ๋กœ ์ฝ”๋“œ ํŒจํ„ด์„ ์ •์˜ํ•˜์—ฌ ์†Œ๊ฐœํ•˜๊ธฐ๋„ ํ•œ๋‹ค. ์ด ํ”„๋ก์‹œ ์ฝ”๋“œ ํŒจํ„ด์€ ์‹ค๋ฌด์—์„œ ์ •๋ง ๋นˆ๋ฒˆํ•˜๊ฒŒ ๋‹ค๋ค„์ง€๊ธฐ ๋•Œ๋ฌธ์—, ์ด์— ๊ฐ๊ฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์—์„œ๋Š” ๋ณ„๋„์˜ ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด์„œ ์•„์˜ˆ ๊ฐœ๋ฐœ์ž์—๊ฒŒ API๋กœ ์ œ๊ณตํ•ด์ค€๋‹ค. ๋Œ€ํ‘œ์ ์œผ๋กœ ์ž๋ฐ”(Java) ์ง„์˜์˜ Dynamic Proxy ๋ฅผ ๋“ค ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ(JavaScript) ์ง„์˜์—์„œ๋„ ES2015๋ถ€ํ„ฐ ๋ณ„๋„์˜ Proxy ๊ฐ์ฒด๋ฅผ ์ง€์›ํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ๋‹ค.

javascript-Proxy-Reflect

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ์˜ 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);

javascript-Proxy-Reflect

์ด๋•Œ ์ฃผ์˜ํ• ์ ์€ ํด๋ผ์ด์–ธํŠธ๋Š” ํ”„๋ก์‹œ ๊ฐ์ฒด์ธ proxyUser๋ฅผ ํ†ตํ•ด ์ ‘๊ทผํ•ด์•ผ ๋œ๋‹ค๋Š” ์ ์ด๋‹ค. ์™œ๋ƒํ•˜๋ฉด ์œ„์˜ ๊ทธ๋ฆผ๊ณผ ๊ฐ™์ด user ๊ฐ์ฒด๋ฅผ ํ”„๋ก์‹œ ๊ฐ์ฒด๊ฐ€ ๊ฐ์‹ธ๊ณ  ์žˆ๋Š” ํ˜•ํƒœ์ด๊ธฐ ๋•Œ๋ฌธ์— ์™ธ๋ถ€ ํฌ์žฅ๊ณผ ๊ฐ™์€ ํ”„๋ก์‹œ๋ฅผ ํ†ตํ•ด ๋Œ€์ƒ ๊ฐ์ฒด์ธ user์™€ ํ†ต์‹ ํ•˜๊ฒŒ ๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ฝ˜์†” ๋กœ๊ทธ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์›๋ž˜๋Š” ๊ฐ’๋งŒ ๋”ธ๋ž‘ ๋‚˜์™€์•ผ ๋˜๋Š”๊ฒƒ์ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ถ”๊ฐ€์ ์œผ๋กœ ์ฝ”๋“œ๊ฐ€ ๊ฐ€๋ฏธ๋จ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

javascript-Proxy

์ด์ฒ˜๋Ÿผ 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);

javascript-Proxy
javascript-Proxy

 

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

javascript-Proxy
javascript-Proxy

 

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) {
}

javascript-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); // ์‚ญ์ œ๊ฐ€ ๋˜์ง€์•Š๊ณ  ๊ทธ๋Œ€๋กœ ๋‚จ์Œ

javascript-Proxy

 

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();

javascript-Proxy


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-Proxy


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
Reflect-js

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

javascript-Proxy


์กด์žฌํ•˜์ง€ ์•Š์€ ํ”„๋กœํผํ‹ฐ๋Š” ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œํ‚ค๊ธฐ

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ์œ ์—ฐํ•œ ์–ธ์–ด์ด๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ์ฒด์— ๋“ฑ๋ก๋˜์ง€ ์•Š์€ ํ”„๋กœํผํ‹ฐ๋ผ๋„ ์ ‘๊ทผํ•˜๋ฉด ๊ทธ๋ƒฅ 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"

javascript-Proxy


์Œ์ˆ˜ ์ธ๋ฑ์Šค ๊ตฌํ˜„ํ•˜๊ธฐ

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ๋ฐฐ์—ด ์ธ๋ฑ์Šค๋Š” ํŒŒ์ด์ฌ ๊ณผ๋Š” ๋‹ฌ๋ฆฌ ์Œ์ˆ˜ ์ธ๋ฑ์Šค๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค. ํ•˜์ง€๋งŒ ์ด๋ฅผ ํ”„๋ก์‹œ๋ฅผ ์ด์šฉํ•ด ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

let array = ["ํ™๊ธธ๋™", "์ž„๊บฝ์ •", "๋ฐ•ํ˜๊ฑฐ์„ธ"];

// ๋งˆ์ง€๋ง‰ ์š”์†Œ : ๋ฐ•ํ˜๊ฑฐ์„ธ
console.log('array[-1]: ', array[-1]);

// ๋’ค์—์„œ ๋‘ ๋ฒˆ์งธ ์š”์†Œ : ์ž„๊บฝ์ •
console.log('array[-2]: ', array[-2]);

// ๋’ค์—์„œ ์„ธ ๋ฒˆ์งธ ์š”์†Œ : ํ™๊ธธ๋™
console.log('array[-3]: ', array[-3]);

javascript-Proxy

๊ฐ„๋‹จํ•˜๊ฒŒ ํ”„๋ก์‹œ 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]);

javascript-Proxy


# ์ฐธ๊ณ ์ž๋ฃŒ

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