본문으로 바로가기

Reflect?

reflecting이란 코드가 구동시(런타임) 자기 자신을 검사, 수정할 수 있는 능력을 말한다.

위키피디아에서 확인해보면, 언어별 reflect 방법을 소개한 것을 확인할 수 있다.

Reflect라는 영단어의 본래 의미를 생각해본다면, "자기 자신을 반추하다" 정도로 이해할 수 있을 것 같다.

 

reflection programming is the ability of a process to examine, introspect, and modify its own structure and behavior.[
출처 : https://en.wikipedia.org/wiki/Reflective_programming

 

vanilla javascript에서도 Reflect는 기본 내장 객체로 존재한다.

이 기능이 부족해서 decorator와 함께 쓰이는 reflect-metadata pollyfill로 강화하여 사용하기도 한다.

자세하게는 아래 Decorator 포스트를 살펴보자.

 

darrengwon.tistory.com/1128

 

@Decorator (3) : reflect-metadata

typescript-kr.github.io/pages/decorators.html - 메타데이터 (Metadata) 부분을 참고합시다. www.npmjs.com/package/reflect-metadata reflect metadata에 들어가기에 앞서... 왜 쓰냐?는 질문에 runtime reflec..

darrengwon.tistory.com

 

여기서는 Reflect와 Proxy에 대해 알아보도록하자

 

1. Reflect 표준 내장 객체

developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Reflect

 

Reflect - JavaScript | MDN

Reflect는 중간에서 가로챌 수 있는 JavaScript 작업에 대한 메서드를 제공하는 내장 객체입니다. 메서드의 종류는 프록시 처리기와 동일합니다. Reflect는 함수 객체가 아니므로 생성자로 사용할 수

developer.mozilla.org

* 다른 대부분의 전역 객체와 다르게, Reflect는 생성자가 아닙니다. 따라서 함수처럼 호출하거나 new 연산자로 인스턴스를 만들 수 없습니다. Math 객체처럼, Reflect의 모든 속성과 메서드는 정적(static)입니다.

 

* Object 객체에도 있는 메서드들이 보이는데, Reflect가 예전에 존재해왔던 메서드들은 묶은 것이기 때문입니다.

 

* IE 만 아니면 브라우저에서 다 실행 가능합니다. 

 

* TS 환경에서 Reflect를 사용하려면 target을 es6 이상으로 설정하시거나(lib로 dom, es6, dom.iterable, scripthost을 사용함.) lib에 "DOM.Iterable"을 추가하셔야 합니다.

 

 

간단한 사용법

 

Reflect.has(obj, key)

특정 객체가 어떤 key 값을 가지고 있는지 

const duck = {
  name: "Maurice",
  color: "white",
  greeting: function () {
    console.log(`Quaaaack! My name is ${this.name}`);
  },
};

Reflect.has(duck, "color"); // true
Reflect.has(duck, "haircut"); // false

 

Reflect.ownKeys(obj)

객체의 키를 배열로 만들기(Object.keys와 같음)

const duck = {
  name: "Maurice",
  color: "white",
  greeting: function () {
    console.log(`Quaaaack! My name is ${this.name}`);
  },
};

Reflect.ownKeys(duck); // [ "name", "color", "greeting" ]
Object.keys(duck); // [ "name", "color", "greeting" ]

 

Reflect.set(obj, key, value)

키:값 전달

const duck = {
  name: "Maurice",
  color: "white",
  greeting: function () {
    console.log(`Quaaaack! My name is ${this.name}`);
  },
};

Reflect.set(duck, "eyes", "black");
Reflect.set(duck, "color", "black"); // overwrite가 됨

 

한 눈에 보는 예시들.

const a = {
  name: 'darren',
  job: 'writer',
  num: 1.75,
};

console.log(Reflect.has(a, 'name')); // true/false
console.log(Reflect.get(a, 'name')); // darren
console.log(Reflect.set(a, 'Date', new Date())); // true/false (성공 여부)
console.log(Reflect.ownKeys(a)); // Object.keys와 같은 결과
console.log(Reflect.apply(Math.floor, undefined, [a.num])); // 1.75에 Math.floore 적용. ArrayLike를 받음

 

 

이 외에도 많은 메서드가 있지만 필요할 때마다 아래 포스트를 참고하자.

www.zerocho.com/category/ECMAScript/post/57ce7fec2a00e600151f085c

 

정의 부분은 아래 같이 생겼으니 이것을 참고해도 좋다.

declare namespace Reflect {
    function apply(target: Function, thisArgument: any, argumentsList: ArrayLike<any>): any;
    function construct(target: Function, argumentsList: ArrayLike<any>, newTarget?: any): any;
    function defineProperty(target: object, propertyKey: PropertyKey, attributes: PropertyDescriptor): boolean;
    function deleteProperty(target: object, propertyKey: PropertyKey): boolean;
    function get(target: object, propertyKey: PropertyKey, receiver?: any): any;
    function getOwnPropertyDescriptor(target: object, propertyKey: PropertyKey): PropertyDescriptor | undefined;
    function getPrototypeOf(target: object): object;
    function has(target: object, propertyKey: PropertyKey): boolean;
    function isExtensible(target: object): boolean;
    function ownKeys(target: object): PropertyKey[];
    function preventExtensions(target: object): boolean;
    function set(target: object, propertyKey: PropertyKey, value: any, receiver?: any): boolean;
    function setPrototypeOf(target: object, proto: any): boolean;
}

 

Object보다 Reflect가 더 자연스러워서 Object 대신 사용하는 경우도 많다고 합니다.

 

2. Proxy 표준 내장 객체

developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Proxy

 

Proxy - JavaScript | MDN

Proxy Proxy 객체는 기본적인 동작(속성 접근, 할당, 순회, 열거, 함수 호출 등)의 새로운 행동을 정의할 때 사용합니다. handler trap들을 가지고 있는 Placeholder 객체. traps 프로퍼티에 접근할 수 있는

developer.mozilla.org

 

MDN의 설명에 따르면 Proxy 객체는 기본적인 동작(속성 접근, 할당, 순회, 열거, 함수 호출 등)의 새로운 행동을 정의할 때 사용한다고 한다.

아! 그렇다면, Object가 가진 기본적인 동작들을 재정의하는데 사용한다고 이해하시면 될 것 같습니다.

그래서 Proxy란 이름이 붙은 거라고 유추할 수 있겠습니다. 기본 동작을 하기 전에 앞단에서 인터셉트해서 다른 동작을 만들어 내는 것이죠.

 

new Proxy(target, handler);

 

handler에 상응하는 트랩이 있으면 트랩이 실행되어 프락시가 해당 작업을  처리하고 트랩이 없으면 작업은 target에 직접 수행됩니다.

말로 하니까 이해가 안되죠? 직접 예시를 보면서 작업해봅시다.

const me = {
  name: 'darren',
  job: 'programmer',
  age: 120,
};

const proxiedMe = new Proxy(me, {
  get: function () {
    console.log('저 proxy입니다.');
    return "haha no you can't";
  },
});

console.log(proxiedMe.name); // 저 proxy입니다. haha no you can't
console.log(proxiedMe.job); // 저 proxy입니다. haha no you can't
console.log(proxiedMe.age); // 저 proxy입니다. haha no you can't

 

handler 부분에서 get은 프로퍼티를 읽을 때 작동하는 내부 메서드입니다. Proxy를 거친 후의 객체인 proxiedMe에서 어떤 프로퍼티를 읽더라도 handler에 작성한 trap이 발동되어서 다른 값을 호출하게 되는 것이죠.

🚨 추측할 수 있듯, Proxy를 씌운 객체가 있다면, 본래 객체를 바꾸는 일은 자제해야 합니다. 코드의 동작을 예측하기가 어려워지기 때문입니다.

 

이렇게 교체 가능한 내부 메서드들은 다음과 같은 값들이 존재합니다.

 

https://ko.javascript.info/proxy

 

조금 생각해보면, 인터셉팅하는 Proxt를 이용해서 여러가지 재미있는 일들을 해볼 수 있을 것 같습니다.

대표적으로, 어떤 값에 대한 validation을 Proxy를 거쳐서 한다던가, 조건에 따라 다른 값을 출력하는 그런 일들이요

 

아래와 같은 코드들로, get할 때마다 numberOption을 체크하는 로직을 넣어줄 수 있습니다.

const numberOption = [0, 1, 2, 3];

// 재할당하기 위해서 let을 사용합시다. 원본 객체를 이용할 수 있으면 오히려 더 혼란을 줍니다.
let order = {
  option: 6,
  product: '네스프레소',
};

order = new Proxy(order, {
  get: function (target, property) {
    console.log(target); // proxy할 객체, 즉, target. { option: 6, product: '네스프레소' }
    console.log(property); // get할 property 이름.
    if (property === 'option') {
      if (property in numberOption) {
        return property;
      } else {
        return 'invalide Option';
      }
    }
    return property;
  },
});

console.log(order.option); // 'invalide Option'

 

그 외의 다른 프로퍼티들을 덮어쓰고 활용하는 방법에 대해서는 ko.javascript.info/proxy#ref-559 를 참고해봅시다.

 

 

 

참고하면 좋은 글)

 

 아 좋은 글이 너무 많네요.

 

ui.toast.com/weekly-pick/ko_20210413

 

pks2974.medium.com/javascript-proxy-%EC%99%80-reflect-%EA%B0%84%EB%8B%A8-%EC%A0%95%EB%A6%AC%ED%95%98%EA%B8%B0-5f1ccaa51b2e

 

ko.javascript.info/proxy

 

medium.com/intrinsic/proxies-and-reflection-in-javascript-334412028f69

 


darren, dev blog
블로그 이미지 DarrenKwonDev 님의 블로그
VISITOR 오늘 / 전체