본문 바로가기
📌 Front End/└ JavaScript

[JavaScript] 프록시(Proxy) 객체, 핸들러 메서드 - 객체 보호, 함수 호출 제어, 생성자 제어, 값 제한, 읽기 전용

by 쫄리_ 2024. 11. 13.
728x90
반응형

📌 프록시(Proxy) 란?

프록시(Proxy)는 JavaScript에서 원본 객체의 동작을 가로채어 제어할 수 있는 기능입니다.

이를 통해 객체의 속성 읽기, 쓰기, 삭제, 함수 호출 같은 작업을

중간에서 조작하여 객체 보호, 동작 커스터마이징, 유연한 관리가 가능합니다.

주로 보안이 중요한 정보 보호복잡한 데이터 구조 관리에서 유용합니다.

 

🔎 프록시 주요 역할

  • 객체 보호 : 민감한 정보 보호
  • 동작 커스터마이징 : 속성 접근 방식 조정
  • 유연한 관리 : 객체를 수정하지 않고 원하는 동작 추가

🔎 프록시 특징

  • 대리인 역할 : 원본 객체 대신 요청을 받고 결과를 반환
  • 명령 재정의 : 속성 접근, 삭제 등 작업을 원하는 대로 변경

 


📌 프록시(Proxy) 객체

원본 객체의 기본 동작(속성 접근, 할당, 함수 호출 등)을 가로채고 제어할 수 있는 객체입니다.

프록시 객체를 생성하려면 new Proxy(target, handler) 사용하며, 두 인자 필요합니다.

  • target : 프록시로 감싸는 원본 객체
  • handler : 원본 객체의 동작을 가로채는 함수(트랩*)이 정의된 객체
const proxy = new Proxy(target, handler);
new Proxy(원본객체/함수, 핸들러함수);

 

💡 트랩(Trap)

트랩은 프록시가 동작을 가로채기 위해 설정하는 함수로, 기본 명령을 재정의하거나 제어할 수 있게 합니다.


📌 프록시 트랩 핸들러(handler) 메서드

프록시의 handler 객체특정 동작을 가로채고 제어할 수 있는 메서드들을 정의합니다.

가로챌 수 있는 동작에는 객체의 속성 접근, 함수 호출, 프로토타입 조작 등이 있습니다.

이때 사용하는 핸들러의 메서드를 트랩(trap)이라고 부릅니다.

트랩 메서드는 속성 접근, 할당, 함수 호출 등 원본 객체의 다양한 동작을 가로채어 재정의하거나 제어


🔎 Property(객체 속성) 가로채는 트랩

  • get() 함수 : 객체의 속성에 접근할 때 작업을 가로챔
  • set() 함수 : 객체 속성에 값을 할당할 때 작업을 가로챔
  • has() 함수 : 객체에 in 연산자가 사용될 때 작업을 가로챔
  • deleteProperty() 함수 : 객체 속성에 delete 연산자가 사용될 때 작업을 가로챔
핸들러 메서드 작동 시점
get(target, property) 프로퍼티에 접근하여 값을 읽을 때 호출
프록시객체명.속성명

target ➡️ 원본 객체
property ➡️ 접근하려는 속성명
set(target, property, value) 프로퍼티 값을 설정할 때 호출
프록시객체명.속성명 = 변경할값;

target ➡️ 원본 객체
property ➡️ 설정하려는 속성명
value ➡️ 설정할 값
has(target, property) in 연산자가 사용될 때 호출(속성 존재 여부 확인)
속성명 in 프록시객체명

target ➡️ 원본 객체
property ➡️ 확인하려는 속성명
deleteProperty(target, property) delete 연산자가 사용될 때 호출
delete 프록시객체명.속성명;

target ➡️ 원본 객체
property ➡️ 삭제하려는 속성명

 

🔎 Method(함수) 가로채는 트랩

  • apply() 함수 : 함수가 호출될 때 동작을 가로챔
  • construct() 함수 : new 연산자를 사용해 생성자 함수를 호출할 때 작업을 가로챔
핸들러 메서드 작동 시점
apply(target, thisArg, args) 함수가 호출될 때
프록시함수명(매개인자1, 매개인자2, ...);

target ➡️ 원본 함수
thisArg ➡️ 함수 호출 시 사용될 this 값
args ➡️ 함수에 전달된 매개변수 목록
construct(target, args) 생성자가 호출될 때 (new 연산자 사용 시)
new 프록시객체명(매개인자1, 매개인자2, ...);

target ➡️ 원본 생성자 함수
args ➡️ 생성자에 전달된 매개변수 목록

 


✏️ 프록시(Proxy) 사용방법

프록시는 객체나 함수의 접근, 변경, 삭제 등의 동작을 가로채고 원하는 로직을 추가할 수 있는 기능

이를 통해 데이터 보호, 함수 호출 제어, 값 검증 등 다양한 작업을 수행할 수 있습니다.

프록시가 설정된 객체나 함수에서 특정 작업이 발생하면,

자바스크립트는 해당 작업에 연결된 트랩 메서드를 자동으로 호출합니다.

트랩 메서드가 있으면 이를 실행하고, 없으면 원래 동작을 그대로 수행합니다.

  1. 객체형 프록시 : 객체 보호/제어 (get, set, has, deleteProperty)
  2. 함수형 프록시 : 함수 호출 제어 (apply)
  3. 생성자 프록시 : 객체 생성 제어 (construct)
  4. 값 검증 프록시 : 특정 값 제한 (set)
  5. 읽기 전용 프록시 : 읽기 전용 객체 (set)
  6. 읽기 전용 프록시 : 읽기 전용 함수 (apply)

1. 객체형 프록시 : 객체 보호/제어 (get, set, has, deleteProperty)

프록시는 원본 객체에 대한 접근을 가로채 특정 조건에 따라 제어할 수 있습니다.

originalUser 객체에서 pw 속성은 항상 ******로 표시되게 하고,

realpw로 접근할 때만 실제 비밀번호 보여줄 수 있습니다.

이렇게 설정하면 proxyUser.pw는 마스킹된 값만 표시되고,

proxyUser.realpw 통해서만 실제 비밀번호를 확인할 수 있습니다.

 

  • get : 프록시 객체의 속성에 접근할 때 get 트랩 자동 실행
    예) proxyUser.pw 접근 시   ➡️  get 트랩 작동
  • set : 프록시 객체의 속성에 값을 할당할 때 set 트랩 자동 실행
    예) proxyUser.id = '소현';  ➡️  할당 시 set 트랩 작동
  • deleteProperty : delete 연산자를 사용할 때 deleteProperty 트랩 자동 실행
    예) delete proxyUser.id; 실행 시  ➡️  deleteProperty 트랩 작동
// 📜index.js

// 1️⃣ 원본 객체 생성
const originalUser = { id: 'root', pw: '1234' };

// 2️⃣ 프록시 핸들러 설정
const handler = {
    // ● 속성 접근 시 동작 설정
    get(origin, props) {
        if (props === 'pw') return '******'; // 'pw' 접근 시 ****** 반환
        else if (props === 'realpw') return origin.pw; // 'realpw' 접근 시 실제 pw반환
        else return origin[props];
    },
    // ● 속성 변경 시 동작 설정
    set(origin, props, value) {
        if (props === 'pw') return; // 'pw' 속성은 변경 금지
        else origin[props] = value; // 다른 속성은 변경 허용
    },
    // ● 속성 존재 확인 시 동작 설정
    has(origin, props) {
        if (props === 'pw') return false; // 'pw' 속성이 있는지 확인 시 false 반환
        return true; // 다른 속성은 true 반환
    },
    // ● 속성 삭제 시 동작 설정
    deleteProperty(origin, props) {
        delete origin[props]; // 실제 속성 삭제
    }
};

// 3️⃣ 프록시 객체 생성
const proxyUser = new Proxy(originalUser, handler);


// 4️⃣ 프록시 객체의 메서드 호출
// ➡️ get 메서드
// get 메서드로 pw 속성 접근 시 ******가 반환되고, realpw 접근 시 실제 비밀번호 반환
console.log(proxyUser.id);       // 'root' 출력
console.log(proxyUser.pw);       // '******' 출력
console.log(proxyUser.realpw);   // 실제 pw '1234' 출력

// ➡️ set 메서드
// set 메서드로 id는 수정할 수 있지만, pw는 변경이 허용되지 않습니다.
proxyUser.id = '소현';           // id는 변경 허용
proxyUser.pw = '7890';           // pw는 변경되지 않음
console.log(proxyUser.id);       // '소현' 출력
console.log(proxyUser.pw);       // '******' 출력
console.log(proxyUser.realpw);   // 실제 pw '1234' 출력

// ➡️ has 메서드
// has 메서드를 통해 in 연산자를 사용할 때 pw는 항상 존재하지 않는 것처럼 표시됩니다.
console.log('id' in proxyUser);  // true 출력
console.log('pw' in proxyUser);  // false 출력 (has 메서드에서 false 반환)

// ➡️ deleteProperty 메서드
// deleteProperty 메서드를 통해 id 속성이 삭제됩니다.
delete proxyUser.id;             // id 속성 삭제
console.log(proxyUser.id);       // undefined 출력 (id가 삭제되었기 때문에 undefined)

 


2. 함수형 프록시 : 함수 호출 제어 (apply)

함수 originalMinMax 프록시로 감싸, 전달된 인자의 순서를 자동으로 조정합니다.

apply 메서드를 사용하여 함수가 호출될 때 동작을 제어합니다.

프록시 proxyMinMax로 감싼 originalMinMax 함수인자 순서를 자동으로 조정합니다.

 

  • apply : 프록시 객체가 함수로 호출될 때 apply 트랩 자동 실행
    예) proxyMinMax(5, 3); 호출 시  ➡️  apply 트랩 작동
// 1️⃣ 원본 함수 정의
// originalMinMax 함수는 min부터 max까지의 숫자를 순서대로 출력
function originalMinMax(min, max) {
    for (let i = min; i <= max; i += 1) {
        console.log(i);
    }
}

// 2️⃣ 프록시 핸들러 설정
// apply 트랩은 함수가 호출될 때 인자 순서를 확인하여 필요 시 순서를 자동으로 조정합니다.
const handler = {
    apply(origin, thisArg, args) {
        // const [min, max, ...losts] = args;   // 세 번째 이후 인자를 배열로 수집
        const [min, max] = args;
        if (min < max) return origin(min, max); // 순서가 맞으면 그대로 실행
        else return origin(max, min);           // 순서가 반대면 교환하여 실행
    }
};

// 3️⃣ 프록시 객체 생성
// originalMinMax 함수를 프록시로 감싸서 proxyMinMax 객체를 생성하여
// apply 트랩이 적용되도록 합니다.
const proxyMinMax = new Proxy(originalMinMax, handler);

// 4️⃣ 프록시 함수 호출
// 프록시 객체 proxyMinMax를 호출합니다. 
// apply 트랩이 작동하여
// 인자가 5와 3 순서로 전달되더라도, 작은 값부터 큰 값까지 출력되도록 자동 조정됩니다.
proxyMinMax(5, 3); // 3부터 5까지 출력 (3 4 5)

 


3. 생성자 프록시 : 객체 생성 제어 (construct)

생성자 함수 Person 동작을 프록시로 감싸서, 객체 생성 시 추가 작업을 할 수 있습니다.

이 프록시는 new 연산자를 사용할 때 객체 생성 동작을 가로챌 수 있습니다.

💡 construct 트랩에서는 Reflect.construct를 사용해야 올바르게 객체가 생성됩니다.

💡 construct 트랩은 객체 생성 시에만 사용되며, 함수를 클래스처럼 활용할 때 유용합니다.

  • construct : 프록시 객체가 생성자로 호출될 때 construct 트랩 자동 실행
    예) new proxyPerson('소현', '29'); 호출 시  ➡️  construct 트랩 작동
// 1️⃣ 원본 함수 정의
// Person 생성자 함수는 name과 age를 받아 새로운 객체를 생성합니다.
function Person(name, age) {
    this.name = name;
    this.age = age;
}

// 2️⃣ 프록시 핸들러 설정
// construct 트랩은 new 연산자로 호출될 때 실행되며,
// Reflect.construct를 통해 원래 Person 생성자 함수의 동작을 그대로 수행
const handler = {
    construct(origin, args, newTarget) {
        return Reflect.construct(origin, args, newTarget);
    }
};

// 3️⃣ 프록시 객체 생성
// Person 함수를 프록시로 감싸서 proxyPerson 프록시 객체 생성
// 이제 proxyPerson 객체가 new 연산자로 호출될 때마다 construct 트랩 작동
const proxyPerson = new Proxy(Person, handler);

// 4️⃣ 프록시 생성자 호출
// 프록시 객체 proxyPerson을 생성자로 호출하여 새로운 객체를 생성
// construct 트랩이 작동하여 Person 생성자를 통해 객체가 생성
const personInstance = new proxyPerson('소현', '29');
console.log(personInstance); // Person { name: '소현', age: '29' }

 


4. 값 검증 프록시 : 특정 값 제한 (set)

utillity_origin 객체data 속성을 프록시로 감싸서, data 값이 0 미만으로 설정되지 않도록 합니다.

// 1️⃣ 원본 객체 생성
// utillity_origin 객체는 data 속성을 가지고 있으며, 초기 값 50
const utillity_origin = { data: 50 };

// 2️⃣ 프록시 핸들러 설정
// set 트랩은 data 속성에 값을 설정할 때마다 작동하며, 
// data에 0 미만의 값이 할당될 경우 0으로 자동 조정
const handler = {
    set(origin, props, value) {
        if (props === 'data') {
            origin[props] = value < 0 ? 0 : value; // data가 0 미만이면 0으로 설정
        }
    }
};

// 3️⃣ 프록시 객체 생성
// utillity_origin 객체를 프록시로 감싸서, utillity 프록시 객체 생성
// 이제 data 속성에 값을 할당할 때마다 set 트랩이 작동
const utillity = new Proxy(utillity_origin, handler);

// 4️⃣ 프록시 객체 사용
console.log(utillity.data); // 50 출력

// utillity.data에 -50을 설정하면, set 트랩 작동하여 0 설정
utillity.data = -50;
console.log(utillity.data); // 0 출력

// utillity.data에 70을 설정하면, set 트랩 작동하여 70 설정
utillity.data = 70;
console.log(utillity.data); // 70 출력

 


5. 읽기 전용 프록시 : 읽기 전용 객체 (set)

readOnly 함수원본 객체가 수정되지 않도록 읽기 전용 프록시를 생성합니다.

// 1️⃣ 읽기 전용 프록시 생성 함수
// set 트랩에서 설정을 무시하여, 속성 값을 변경할 수 없도록 만듦
function readOnly(origin) {
    return new Proxy(origin, {
        set() { return; } // 설정을 무시하여 읽기 전용으로 만듦
    });
}

// 2️⃣ 읽기 전용 객체 생성
// readOnly 함수를 사용하여 원본 객체가 수정되지 않는 읽기 전용 객체 readusers 생성
const readusers = readOnly({ id: 'root', pw: '1234' });

// 3️⃣ 읽기 전용 속성 변경 시도
// 속성 변경이 무시되므로, readusers 객체는 원본 상태를 유지합니다.
readusers.id = 'admin'; // 변경 시도 무시
console.log(readusers); // { id: 'root', pw: '1234' }

 


6. 읽기 전용 프록시 : 읽기 전용 함수 (apply)

readOnlyFunc 함수전달된 함수(func)를 읽기 전용으로 감싸

함수가 호출될 때 인자로 전달된 객체가 수정되지 않도록 합니다.

이를 통해 함수 내부에서 객체 속성을 변경하려 해도 원본 객체는 유지됩니다.

// 1️⃣ 읽기 전용 함수 생성 함수
// readOnlyFunc 함수는 전달된 함수(func)를 프록시로 감싸서,
// 해당 함수가 호출될 때 인자로 전달된 객체를 읽기 전용으로 변환하여 전달합니다.
function readOnlyFunc(func) {
    return new Proxy(func, {
        apply(origin, thisArg, args) {
            // 함수 호출 시, 인자로 전달된 모든 객체를 읽기 전용으로 변환하여 전달
            const readOnlyArgs = args.map(arg => 
                typeof arg === 'object' ? new Proxy(arg, { set() { return; } }) : arg
            );
            // 읽기 전용 객체로 변환된 인자들로 함수 호출
            return origin.apply(thisArg, readOnlyArgs);
        }
    });
}

// ➡️ 읽기 전용 속성 변경 시도
// modifyUser 함수는 전달받은 user 객체의 id 값을 변경하려고 시도합니다.
function modifyUser(user) {
    user.id = 'modified'; // 시도해도 변경되지 않음(읽기전용)
    console.log(user);    // { id: 'original', pw: '1234' } 출력
}

// 2️⃣ 읽기 전용 함수 생성
// modifyUser 함수를 읽기 전용 함수로 감싼 readOnlyModifyUser 생성
const readOnlyModifyUser = readOnlyFunc(modifyUser);

// 3️⃣ 사용할 객체 생성
const user = { id: 'original', pw: '1234' };

// 4️⃣ 읽기 전용 함수 사용
// user 객체를 전달하여 readOnlyModifyUser를 호출
// modifyUser 함수에서 user.id를 변경하려고 해도 원본 객체는 수정되지 않음
readOnlyModifyUser(user); // { id: 'original', pw: '1234' } 출력

 


728x90
반응형