JSX → React

Virtual DOM

<body>
  <!-- <p>Actual DOM</p> -->
  <script src="./index.js"></script>
</body>

실제 DOM model 컨트롤

// Actual DOM
var actual_dom = document.createElement('p')
actual_dom.innerHTML = 'Actual DOM'
document.body.appendChild(actual_dom)
실제 텍스트가 렌더링 되고 index.js파일도 잘 불러와 진 것을 확인 할 수 있다.

가상 DOM model 컨트롤

virtual-hyperscript은 실제 virtual tree를 만들어주는 역할을 한다. vdom은 실제 DOM에 렌더링하고 알고리즘을 통해 붙이는(patch)작업을 한다.

h('요소', 'innerHTML')
React.createElement('a', {href: './', className: 'link'}, '클릭')

React.createElement()

React.createElement('a', {href: './', className: 'link'}, '클릭')

먼저 h, createElement를 생성한다.

var h = require('virtual-dom/h')
var createElement = require('virtual-dom/create-element')

예시

<p> 요소에 텍스트 'virtual dom'를 넣는다.

var tree = h('p', 'virtual dom')
var rootNode = createElement(tree)
document.body.appendChild(rootNode)
실제 렌더링 되는 결과

요소 안에 여러 요소를 추가 할 때는 []배열로 전달할 수 있다. 또한 배열의 순서대로 출력이 되기 때문에 마크업의 순서를 설정할 수 있다.

var tree = h('div.foo#some-id', [
  '입력해주세요 ',
  h('span', 'some text'),
  h('input', { type: 'text', value: 'foo' }),
])
var rootNode = createElement(tree)
document.body.appendChild(rootNode)
실제 렌더링 되는 결과

클래스와 아이디 추가하기

태그 안에 속성을 넣고 싶을 때 {}를 사용해서 넣는 방법도 있지만 아이디와 클래스를 넣을 때는 요소에 바로 입력해서 넣을 수 있다.

h('div.container', list)
h('div', { className: 'container' }, list)

// 위 둘의 결과 값은 같다. 
<div class="container"></div>

h('div#container', list)
h('div', { id: 'container' }, list)

// 위 둘의 결과 값은 같다. 
<div id="container"></div>

중간 정리

h, create-element를 사용해서 가상 요소를 생성하고 가상 tree를 만들 수 있다.

var h = require('virtual-dom/h')
var createElement = require('virtual-dom/create-element')

// Virtual DOM
var tree = h('p', 'virtual dom')
var rootNode = createElement(tree)
document.body.appendChild(rootNode)


console.log(typeof h)             // function
console.log(typeof createElement) // function
console.log(tree)                 // VirtualNode (가상노드)

가상 DOM 가상 트리 비교 후, 패치

diff()를 사용해서 기존의 treenewtree의 변화를 감지하고 변화된 내용을 변수 patches에 담는다. 그리고 patch()는 변화된 내용을 변수 rootNode에 담아 <body>에 붙인다.

var patches = diff(tree, newTree)
patch(rootNode, patches)
var h = require('virtual-dom/h')
var createElement = require('virtual-dom/create-element')
var diff = require('virtual-dom/diff')
var patch = require('Virtual-dom/patch')

var count = 0

function render(n) {
  return h('p', 'virtual dom ' + n)
}


function update() {
  // 새로운 가상 트리를 생성
  var newTree = render(++count) // 1, 2, 3, ...
  // 기존 가상 트리, 새로운 가상 트리하고 변경점이 있는지 확인
  var patches = diff(tree, newTree)
  // 변경사항이 발생하면 rootNode에 패치(붙인다)
  patch(rootNode, patches)
}

// Virtual DOM
var tree = render(count)
var rootNode = createElement(tree)
document.body.appendChild(rootNode)

window.setInterval(function () {
  update()
}, 1400)
위의 코드에서 각 변수가 가리키는 것을 이미지로 확인

가상 DOM 배열 데이터 순환 처리

var h = require('virtual-dom/h')
var createElement = require('virtual-dom/create-element')
var diff = require('virtual-dom/diff')
var patch = require('Virtual-dom/patch')

var data = ['vue.js', 'angular', 'react']

function render(data) {
  var lists = data.map(function (item, index) {
    return h('li', item)
  })
  return h('ul', lists)
}

// Virtual DOM
var tree = render(data)
var rootNode = createElement(tree)
document.body.appendChild(rootNode)
실제 렌더링 되는 결과

가상 DOM 제거버튼

var data = ['vue.js', 'angular', 'react']

function render(data) {
  var lists = data.map(function (item, index) {
    return h('li', [
      item,
      h(
        'button',
        {
          type: 'button',
          onclick: function (e) {
            data.splice(index, 1)
            console.log(data)
            update()
          },
        },
        'delete'
      ),
    ])
  })
  return h('ul', lists)
}

function update() {
  var newTree = render(data) 
  var patches = diff(tree, newTree)
  patch(rootNode, patches)
}

// Virtual DOM
var tree = render(data)
var rootNode = createElement(tree)
document.body.appendChild(rootNode)

가상 DOM 추가버튼

버튼을 클릭 했을 때 클릭 할 때마다 1, 2, 3, 4, ... 개씩 추가되는 오류를 방지하기 위해 tree=newTree 구문으로 diff()가 비교하는 값을 바꿔줌

function update() {
  var newTree = render(data)
  var patches = diff(tree, newTree)
  patch(rootNode, patches)
  // tree 변수 값 업데이트
  tree = newTree
}
var h = require('virtual-dom/h')
var createElement = require('virtual-dom/create-element')
var diff = require('virtual-dom/diff')
var patch = require('Virtual-dom/patch')

var data = ['vue.js', 'angular', 'react']

function render(data) {
  var lists = data.map(function (item, index) {
    return h('li', [
      item,
      h(
        'button',
        {
          type: 'button',
          onclick: function (e) {
            data.splice(index, 1)
            console.log(data)
            update()
          },
        },
        'delete'
      ),
    ])
  })

  var list = h('ul', lists)

  var input = h('input.add-content', {
    type: 'text',
    placeholder: 'Add Favorite Framework',
  })

  var add_button = h(
    'button.add-button',
    {
      type: 'button',
      onclick: function (e) {
        var input = document.querySelector('.add-content')
        // 모델 데이터 업데이트
        data.push(input.value)
        // 화면 뷰 업데이트
        update()
        // 콘솔 변경된 데이터 출력
        input.value = ''
      },
    },
    'Add'
  )

  var container = h('div.container', [input, add_button, list])
  return container
}

function update() {
  var newTree = render(data)
  var patches = diff(tree, newTree)
  patch(rootNode, patches)
  // tree 변수 값 업데이트
  tree = newTree
}

// Virtual DOM
var tree = render(data)
var rootNode = createElement(tree)
document.body.appendChild(rootNode)

JSX와 Virtual DOM

// JSX
const reactElement = 
  <h1 lang='en' className='headline'>
    <strong>React</strong>{' '}
    <abbr title='Application Programming Interfaces'>APIs</abbr>
  </h1>
  
ReactDOM.render(reactElement, document.getElementById('root'))
  
// Virtual DOM
var h = require('virtual-dom/h')
var createElement = require('virtual-dom/create-element')

var abbr = h('abbr', { title: 'Application Programming Interfaces' }, 'APIs')
var strong = h('strong', 'React')

var head = h('h1. headline', { leng: 'en' }, [strong, abbr])

var rootNode = createElement(head)
document.body.appendChild(rootNode)

JSX

JSX란?

XML과 유사한 JS 문법(구문) 확장이다. 또는 XML 문법을 가진 JS 표현 식으로 말하기도 한다.

JSX가 하는 일

JSX가 하는 일은 React 요소(Element)를 만드는 것이다. React.createElement()로 요소를 생성하고 추가하는 것은 그 과정이 복잡하다 이때, JSX를 사용해서 요소를 생성하면 가독성도 높아지고 손쉽게 요소를 추가할 수 있다.

React 요소는 실제 DOM 요소가 아니라, JavaScript 객체이다.

React 요소를 만드는 2가지 방법

React 요소를 만들 때, JSX를 사용하거나 React.createElement()를 사용해서 만들 수 있다. 하지만 React.createElement()를 사용하는 방법은 복잡하기 때문에 대부분 JSX를 사용하여 작업한다.

const app = (
  <div className="app">
    <h1>React 앱</h1>
  </div>
)
const appHeading = React.createElement(
  'h1',
  null,
  'React.createElement() 메서드'
)

const appElement = React.createElement('div', { class: 'app' }, appHeading)

DOM vs 가상 DOM

ReactDOM 모듈은 가상 DOM을 실제 DOM에 장착(Mount)시켜 렌더링(Rendering)한다.

가상 DOM을 사용하는 이유?

UI는 수시로 변경되는데 변경될 때마다 DOM이 변경되면 업데이트된 요소를 다시 렌더링 하는데 이 과정이 UI의 속도를 느리게 한다. 만약 렌더링 해야하는 요소의 수가 많을 수록 비용은 더 많이 들고 속도는 더 느려진다.

가상 DOM은 실제 DOM에 직접적으로 조작하는 것이 아니라, 이전/이후 상태를 비교하여 변경 사항이 발생할 때 변경된 부분만 실제 DOM에 업데이트(patch) 하므로 UI 속도를 대폭 향상시킬 수 있다.

Babel's Compile

Babel은 JSX를 React.createElement()로 컴파일 한다.

Babel를 사용해서 React.createElement()로 컴파일 한다.

참고

Last updated

Was this helpful?