자바스크립트 Proxy 객체를 사용해서 State 만들어 보자

React를 사용하면 useState를 통해서 데이터를 바꿔서 DOM을 다시 그리는 기능을 사용할 수 있다.

 

생각해보면, 순수 자바스크립트로 화면을 바꾸고자 한다면 DOM에서 element를 찾은 뒤에 데이터를 직접 수정해줘야 하는 부분이다.

 

그래서 어떻게 React는 이런 반응성, Reactivity를 구현했을까 고민해보고 자바스립트의 Proxy 객체를 사용해서 State를 비슷하게 구현해보자 하였다.

 

🙌 직접 구현한 예제 

최대한 React와 비슷하게 구현해보려고 노력했다.

 

다음은 구현한 코드 내용인데, 일단 HTML이다.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Proxy-Reactivity</title>
  </head>
  <body>
    <div class="root"></div>
    <script src="./app.js"></script>
  </body>
</html>

 

root에 element가 삽입되는 것으로 생각했고, 하나의 js를 통해서 이를 통제할 수 있도록 구현해봤다.

 

Proxy를 사용해서 구현했는데, Proxy에 대한 간단한 설명을 남기자면 다음과 같다.

 

  • Proxy는 JavaScript의 객체를 가로채고 그 동작을 변경할 수 있는 강력한 기능이다.
  • 이를 통해 객체의 기본 동작을 수정하거나, 특정 작업이 일어날 때 추가적인 로직을 실행할 수 있도록 로직 수정이 가능하다.
  • 기본적으로 Proxy는 다음과 같은 두 개의 주요 요소로 구성된다.
    • 타겟 객체(target): 가로채려는 원본 객체.
    • 핸들러(handler): 타겟 객체의 특정 동작을 가로채는 함수들로 이루어진 객체.

 

그리고 proxy를 사용한 app.js 파일이다.

// reactivity 설정
const root = document.querySelector(".root");
const stateName = "state-name";

const target = { name: "header", value: 0 };
const handler = {
  get(target, prop, receiver) {
    return Reflect.get(...arguments);
  },
  set(target, prop, receiver) {
    if (target.value === receiver) {
      // DOM의 변화가 없다.
      return;
    }
    target.value = receiver;
    // 실제 DOM이 변경
    const findElements = document.querySelectorAll(
      `[${stateName}=${target.name}]`
    );
    findElements.forEach((element) => {
      element.innerHTML = receiver;
    });
  },
};

// 실제 사용
const proxy = new Proxy(target, handler);

const header = document.createElement("h1");
header.innerText = proxy.value;
header.setAttribute(stateName, proxy.name); // find elements를 찾기 위해서 커스텀 data-속성을 설정해준다.

// 숫자
const increaseButton = document.createElement("button");
increaseButton.innerText = "Increase";
increaseButton.addEventListener("click", () => {
  proxy.value = proxy.value + 1;
});

root.appendChild(header);
root.appendChild(increaseButton);

 

handler를 살펴보면, 만약 데이터가 변하지 않으면 Reflect 기능을 사용해 기존의 get 기능을 실행한다.

 

반면, 데이터의 변경이 있었다고 하면 기존에 설정한 data 속성 값과 target의 name값을 통해서 DOM에 존재하는 변경된 element 들을 가져온다. (상태가 변경됨으로써, 함께 변경되어야 하는 Elements의 목록)

 

외부에서 전달된 receiver 값을 target.value에 전달함으로써, 데이터를 수정할 수 있다.

그리고 해당 elements의 innerText를 수정하여 실제 DOM에 데이터 변경사항이 반영되도록 해준다.

 

코드 하단에 실제 사용 예시를 작성했는데, React의 아주아주 기초적인 예제인 버튼 클릭 시 숫자가 증가하는 코드를 작성해보았다.

 

값을 Proxy 객체를 통해서 전달하고 수정해주면 작성한 reactivity 코드를 사용할 수 있다.

 

 

실제로 html을 실행시켜 보면, increase 버튼을 눌렀을 때 화면의 숫자가 잘 증가하는 것을 확인할 수 있다 ! 👍