바닐라로 상태 기반 렌더링 컴포넌트 만들기 2 - 상태 기반 렌더링은 어떻게 하는걸까?

Sonny·2022년 4월 13일
11

Vanilla

목록 보기
2/5
post-thumbnail

이 시리즈의 이전 글은 아래로...

상태 기반 렌더링은 "어떻게" 할 수 있을까요? 🤔

  • HTML을 먼저 만들어두지 않고, 자바스크립트를 이용해서 렌더링 (SPA 구조의 CSR 방식)
  • 하나의 html로 화면을 그리기 때문에 컴포넌트들은 라이프 사이클을 가지게 됩니다.
  • 각자 상태(state)를 기반으로 렌더링
  • 상태(state)의 흐름은 Flux 패턴을 따릅니다!

TIP) 간단하게 알아보는 용어

  • SPA (Single Page Application): 하나의 html만을 이용하여 렌더링하는 패러다임
  • CSR (Client Side Rendering): 브라우저에서 자바스크립트를 이용하여 렌더링을 하는 렌더링 방식
  • Flux 패턴: 한 방향의 데이터 흐름을 갖는 디자인 패턴 (상위 -> 하위)

자바스크립트로 렌더링할 때 주의사항 🚨

// index.js

const divEl = document.createElement('div');

위와 같이 자바스크립트에서 Element를 만들기만 하면 우리 브라우저는 이 Element의 존재를 모른다..!
html 태그 안에 넣어주지 않으면 그저 메모리 속에 저장되어 있다가 사라질뿐...!

// index.html

<div id="App"></div>
// index.js

const appEl = document.querySelector('#app');

// 1. 만들기
const divEl = document.createElement('div');
// 2. 화면에 표시할 수 있게 넣어주기
appEl.appendChild(divEl)

자바스크립트로 열심히 그려준 Element는 html 태그로 넣어주어야지만 실제로 화면에 보여지게 된다!

JavaScript로 상태기반 렌더링 구현해보기 😊

완성본은 요기!

1. Entry Point 만들어주기

// index.html

<div id="App"></div>

Entry Point: 자바스크립트로 열심히 그린 Element들을 넣어주는 데에 이용이 되는 시작점

2. 최상위 App 컴포넌트 만들어주기

// App.js

function App({ node }) {
  const initalState = {
    count: 0,
    onClick: () => {
      const { count } = this.state

      this.setState({
        ...this.state,
        count: count + 1
      });
    }
  };
  this.state = initalState
  
  this.setState = newState => {
    this.state = {
      ...this.state,
      ...newState
    }
  }
};

new App({ node: appEl });
  • App 컴포넌트는 자체적인 상태(state)를 가지고 있다.
  • 하위 컴포넌트에서는 이벤트를 전달하고 최상위 컴포넌트인 App 컴포넌트는 상태를 변경시킨다.

상태의 흐름과 구조는 위와 같이 간단하다!
자체적으로 상태를 가지고 있고 하위에서 변경이 발생하면 하위에서 변경하는 것이 아니라 상위로 이벤트를 올려주어 상위에서 다시 하위로 갈 수 있게 하는 것!

Q. 상위로 전달하지 않고 하위에서 상태를 바로 변경하면 안될까..?

A. 바꿀 수 있지만 하위 컴포넌트가 복잡해지는 경우, 상태 관리가 어려워짐
=> 서로 다른 상태를 렌더링해주는 문제 발생할 수 있다!

3. Title 컴포넌트 만들기

function TitleEl({ node, initalState }) {
  this.state = initalState;
  const h1El = document.createElement('h1');
  
  this.setState = newState => {
    this.state = {
      ...this.state,
      ...newState
    }
    
    this.render();
  }
  
  this.render = () => {
    h1El.textContent = `Count: ${this.state.count}`;
  }
  
  this.init = () => {
    node.appendChild(h1El);
    this.render();
  }
  
  this.init();
}

Title 컴포넌트는 h1 태그를 만들고 그 안에 현재 Count를 보여주는 컴포넌트이다!
=> 상태가 변경될 때마다 변경되는 상태를 보여주면 끝!

  • 상위 컴포넌트로부터 초기 상태값을 받고 자신의 상태로 동기화를 한다.
  • 화면에 보여질 요소를 createElement로 그린다.
  • setState() : 상태가 변경되면 자신의 상태 변경과 함께 다시 렌더링을 진행한다.
  • render(): 변경된 상태를 반영하여 textContent를 바꾼다.
  • init(): 부모 컴포넌트에 createElement로 만든 요소를 넣고 초기 렌더링을 진행한다.

4. Button 컴포넌트 만들기

function ButtonEl({ node, initalState }) {
  this.state = initalState;
  
  this.init = () => {
    const btnEl = document.createElement('button');
    btnEl.textContent = `Count + 1`;
    btnEl.addEventListener('click', () => {
      this.state.onClick();
    });

    node.appendChild(btnEl);
  }
  
  this.init();
}

Button 컴포넌트는 button 태그를 만들고 클릭 시, 현재 Count 값에 + 1을 하게 해주는 컴포넌트이다.
=> 클릭할 때마다 전달받은 onClick을 실행시켜주면 끝!

  • 상위 컴포넌트로부터 초기 상태값을 받고 자신의 상태로 동기화를 한다.
  • init() : button 엘리먼트 만들고 click 이벤트를 바인딩한 뒤, 초기 렌더링을 진행한다.

5. 하위 컴포넌트들을 App 컴포넌트에 연결하기

잘 만들어진 하위 컴포넌트들을 연결해보쟈!

function App({ node }) {
  const initalState = {
    count: 0,
    onClick: () => {
      const { count } = this.state

      this.setState({
        ...this.state,
        count: count + 1
      });
    }
  };
  this.state = initalState
  
  this.setState = newState => {
    this.state = {
      ...this.state,
      ...newState
    }
    
    TitleComponent.setState(newState);  // TitleComponent의 상태도 같이 변경
  }
  
  // 하위 컴포넌트들 생성
  const ButtonComponent = new ButtonEl({ node, initalState });
  const TitleComponent = new TitleEl({ node, initalState });
};

new App({ node: appEl });
  • setState(): App 컴포넌트의 상태가 변경이 되었을 때, Title 컴포넌트의 상태도 같이 변경
  • App 컴포넌트 생성 시, 하위 컴포넌트들도 같이 생성
    • Button과 Title 컴포넌트는 생성과 동시에 init() 메서드를 호출

이유와 동작하는 형태에 대해 알아보았으니, 다음 편은 바로 TypeScript 환경 세팅 후 프로젝트 시쟉!!!

참고 자료 📚

profile
FrontEnd Developer

0개의 댓글