Frontend/JavaScript

JavaScript의 customElements에서 Proxy을 활용한 객체 변화 감지

엘리브가 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를 직접 수정하지 않아도, 간접적으로 수정하면 그 값이 자동으로 변경되는 것을 확인할 수 있게 됩니다.

반응형