• JavaScript의 customElements에서 Proxy을 활용한 객체 변화 감지
    Frontend/JavaScript 2023. 3. 3. 14:13
    반응형

    앞서 작성한 글을 완벽하게 숙지하고 사용하시다 보면 attribute 값을 업데이트 하는 과정에서 여러 의문이 들 것입니다.

     

    ??? : observedAttributes()attributeChangedCallback() 으로 태그의 attribute 값이 바뀌는 것을 감지해주는건 알겠는데…

    ??? : 여기에 render() 함수를 걸어두면 attribute값이 바뀔 때 렌더링도 자동으로 최신화해주는 것도 알겠는데..

    ??? : attribute는 텍스트만 넘겨줄 수 있고, attribute를 감지하는 것이지 엘리먼트 내부의 상태값을 감지하는 것은 아니지 않나?

     

    객체가 변했을 때 렌더링이 자동으로 일어나게 할 수 없을까?

    customElements을 그렇게 만들어 보겠습니다.

    class MyProxyElement extends HTMLElement {
      constructor() {
        super();
        this.state = new Proxy({ value: 'init value' }, {
          set: (obj, prop, value) => {
            obj[prop] = value;
            console.log('나 바뀜!')
            this.render();
            return true;
          },
        });
        this.render();
      }
    
      render() {
        this.innerHTML = `현재 상태는? : ${this.state.value}`;
      }
    }
    
    export default MyProxyElement;

    어떤 state를 Proxy로 작성하게 되면, 특정 상태를 감시할 수 있습니다.

    Proxy 객체를 사용하면 어떤 객체의 작업을 가로채서 재정의를 할 수 있습니다.

    이 객체에 대한 설명은 MDN이나 모던JS 문서에도 잘 나와있지만,

    이 블로그에 있는 글도 다양한 사용법을 소개해줍니다.

    위의 문서를 참고해보면 다음과 같은 해석이 가능합니다.

    this.state에 어떤 객체 { value: 'init value' }를 감시 대상자로 지정하고, 그 객체가 특정한 어떤 행동을 했음을 감지했을 때, 다음 행동으로 동작하게 합니다.

    이 설명이 다소 복잡하여 좀 더 상세하게 예시를 들면

    // 위 클래스의 생성자 내부를 가져온 것 입니다.
    this.state = new Proxy({ value: 'init value' }, {
      set: (obj, prop, value) => {
        obj[prop] = value;
        console.log('나 바뀜!')
        this.render();
        return true;
      },
    });

    클래스 필드로 상태를 등록할 때, Proxy가 중간에 value를 가로채서 변화를 감지할 수 있습니다.

    get 옵션을 준다면 값을 읽어들일 때, set 옵션을 준다면 값을 재정의할때 등 여러 상황의 이벤트를 감지할 수 있게 됩니다.

     

    즉, 초기값으로 this.state.value는 ‘init value’를 가지게 될 것입니다.

    하지만 이 객체는 그냥 객체가 아닌 Proxy 객체에 의해 감시를 받고 있게 됩니다.

    바깥에서는 이 객체가 감시 받는 중인지 잘 모르겠지만요!

    (감시라는 표현이 맞는지 모르겠지만 이해를 돕기 위해 이렇게 설명합니다.)

     

    위에서 만든 클래스를 정의해주고

    customElements.define('my-proxy', MyProxyElement);

    html에서도 호출해봅니다.

    <my-proxy id="myProxy"></my-proxy>

    그리고 이 객체의 상태를 업데이트 하기 위한 버튼을 생성 해보겠습니다.

    <button onclick="changeProxyState()">상태를 바꿔보자</button>
    function changeProxyState() {
        const myProxy = document.getElementById('myProxy');
        myProxy.state.value = 'new value'
    }

    그리고 이 버튼을 눌러보면

    현재 상태는? : init value

    에서

    현재 상태는? : new value

    으로 스스로 바뀌는 것을 확인할 수 있습니다.

    Proxy가 렌더 함수를 트리거로 동작한다

    이 버튼은 그저 단순히 이 엘리먼트의 value 값을 수정했을 뿐인데, 왜 재렌더링이 됐을까요?

    콘솔창도 열어보면, 버튼을 눌렀을 때 나 바뀜! 도 출력됨을 확인할 수 있습니다.

    즉, Proxy객체의 setter가 value 값이 새로 쓰일 때를 감지하여 재 렌더링을 유도했음을 확인할 수 있습니다.

    꼭 위의 예제처럼 state를 직접 수정하지 않아도, 간접적으로 수정하면 그 값이 자동으로 변경되는 것을 확인할 수 있게 됩니다.

    반응형

    댓글