Virtual DOM

실제 DOM의 사본 같은 개념. React는 실제 DOM 객체에 접근하여 조작하는 대신 이 가상의 DOM 객체에 접근하여 변화 전/후를 비교 후 바뀐 부분을 적용한다

Real DOM

DOM (Document Object Model)
JavaScript 같은 스크립팅 언어가 <html>, <head>, <body>와 같은 태그들에 접근하고 조작할 수 있도록 브라우저가 태그들을 트리 구조로 객체화 시킨 것
브라우저가 트리구조로 만든 객체 모델

DOM 조작속도가 느려지는 이유

트리는 "데이터 저장" 자체의 의미보다 저장된 데이터를 더 효과적으로 탐색하기 위한 용도로 사용됨 => 빠른 자료 탐색 성능이 장점!

DOM의 변경과 업데이트로 인한 브라우저의 리플로우(Reflow)/리페인트 과정은 속도를 느리게 만드는 원인

=> JavaScript로 조작하는 DOM 요소가 많을수록 모든 DOM 업데이트에 대하여 리플로우하게된다는 뜻 => 업데이트된 부분만 렌더링 할 수 없을까? ==> Virtual DOM 탄생배경

Virtual DOM 작동방식

가상 DOM은 가상의 UI 요소를 메모리에 유지시키고 이를 ReactDOM과 같은 라이브러리를 통해 실제 DOM과 동기회시키는 것
=> 실제 DOM처럼 브라우저에 실제로 그리는 것이 아니기 때문에 속도가 훨씬 빠름

  1. 새로운 요소가 UI에 추가
  2. 트리구조의 가상 DOM이 만들어지고 가상 DOM 트리가 만들어짐
  3. 이전 가상 DOM과 이후 가상 DOM의 차이를 비교
  4. 가상 DOM은 실제 DOM에 변경을 적용할 수 있는 최상의 방법을 계산
  5. 최적의 방법으로 렌더링 수행 가능
  6. 더 빠른 렌더링 가능

React Diffing Algorithm (비교 알고리즘)

React의 DOM 트리 순회방식

트리의 레벨 순서대로 순회함 (같은 레벨-위치끼리 비교) => BFS(너비우선탐색)

다른 타입의 DOM 엘리먼트인 경우

<div><span>으로 바뀌는 등 부모 태그가 달라지면,
이전 트리를 버리고 새로운 트리를 구축해버림.

같은 타입의 DOM 엘리먼트인 경우

태그 자체가 바뀌지 않고 className만 바뀌는 경우 그로 인해 변경된 스타일만 수정하고 다른 요소는 수정하지 않음

하나의 DOM 노드를 처리한 뒤 뒤이어 해당 노드들 맡의 자식들을 순차적으로 동시에 순회하면서 차이가 발견될 때 변경함 => 재귀적으로 처리

<ul>
  <li>Duke</li>
  <li>Villanova</li>
</ul>
//자식 엘리먼트를 처음에 추가합니다.
<ul>
  <li>Connecticut</li>
  <li>Duke</li>
  <li>Villanova</li>
</ul>

React는 위에서 아래로 순차적으로 탐색함
자식 엘리먼트를 맨 위에 추가하는 경우, 리액트는 리스트 전체가 바뀌었다고 받아들이고 전부 버리고 새롭게 렌더링을 해버림==> 매우 비효율적인 동작 방식

키(Key)가 있는 경우

기존 트리의 자식과 새로운 트리의 자식이 일치하는지 key를 통해 확인할 수 있음
키는 전역적으로 유일할 필요는 없고 형제 엘리먼트 사이에서만 유일하면 됨

<ul>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

//key가 2014인 자식 엘리먼트를 처음에 추가합니다.
<ul>
  <li key="2014">Connecticut</li>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

shortid/ nanoid

shortid.generate()


React Hooks

Function Component와 Class Component

Class Component 예시

class Counter extends Component {
    constructor(props) {
        super(props);
        this.state = {
            counter: 0
        }
        this.handleIncrease = this.handleIncrease.bind(this);
    }
    handleIncrease = () => {
        this.setState({
            counter: this.state.counter + 1
        })
    }
    render(){
       return (
            <div>
                <p>You clicked {this.state.counter} times</p>
                <button onClick={this.handleIncrease}>
                    Click me
                </button>
            </div>
       ) 
    }
}

단순한 counting하는 기능만 구현한 것인데도 매우매우 복잡함

Function Component 예시

function Counter () {
    const [counter, setCounter] = useState(0);
    const handleIncrease = () => {
        setCounter(counter + 1)
    }
    return (
        <div>
            <p>You clicked {counter} times</p>
            <button onClick={handleIncrease}>
                Click me
            </button>
        </div>
    )
}

일단 보는 눈이 편안...😌
훨씬 직관적이고 보기 쉬움

Hook 이란?

함수형 컴포넌트에서 상태값 및 다른 여러 기능 사용을 편리하게 해주는 메소드
클래스형 컴포넌트에서는 동작하지 않음

Hook 사용 규칙

1. 리액트 함수의 최상위에서만 호출해야함

반복문, 조건문, 중첩된 함수 내에서 Hook 실행 시 예상대로 동작하지 않을 수도 있음

2. 오직 리액트 함수 내에서만 사용되어야 함

리액트 함수형 컴포넌트나 커스텀 Hook이 아닌 다른 일반 JavaScript안에서 호출 안됨


useMemo, useCallback

렌더링 최적화를 위한 Hook

useMemo

특정 값을 재사용할 때 사용하는 Hook

고정된 값을 가져다 쓰는 경우, 그 값을 어딘가에 저장해뒀다가 꺼내서 쓰는 개념
=> 그렇게 되면 값이 필요할 때마다 매번 해당함수를 호출하지 않아도 됨!

/* useMemo를 사용하기 전에는 꼭 import해서 불러와야 합니다. */
import { useMemo } from "react";

function Calculator({value}){

	const result = useMemo(() => calculate(value), [value]);

	return <>
      <div>
					{result}
      </div>
  </>;
}

컴포넌트도 감쌀 수 있음
난수생성방법:

Math.randon().toString(36).slice(2)

색깔 랜덤 생성함수: Hex, Math.floor(), Math.random(), 16진법

`#${Math.floor(Math.random()*16777215).toString(16)}` //#265bb3 과 같이 색상번호로 표기됨

useEffect vs useMemo

useEffect : fetch(API) 해온 값을 초기에 렌더링 시킬때 주로 사용
useMemo : 그 외에 값들에 주로 사용, 시간이 많이 걸리는 연산들

Memoization

기존에 수행한 연산 결과값을 메모리에 저장해두고 동일한 입력이 들어오면 재활용하는 기법
메모이제이션을 활용할 경우 굳이 중복 연산을 할 필요가 없으므로 앱의 성능을 최적화할 수 있음


useCallback

useMemo는 값의 재사용을 위해 사용하는 Hook인 반면, useCallback은 함수의 재사용을 위해 사용하는 Hook

/* useCallback를 사용하기 전에는 꼭 import해서 불러와야 합니다. */
import React, { useCallback } from "react";

function Calculator({x, y}){

	const add = useCallback(() => x + y, [x, y]);

	return <>
      <div>
					{add()}
      </div>
  </>;
}

참조 동등성

새로이 만들어 호출된 함수는 기존의 함수와 같은 함수가 아님. 주소값이 달라짐.
그 주소값까지 저장을 해버리는 것이 useCallback
useCallback을 이용해 함수 자체를 저장해서 다시 사용하면 함수의 메모리 주소 값을 저장했다가 다시 사용한다는 것과 같은 것 => 주소 값까지 저장하여 재선언을 막는 기능을 함
따라서 React 컴포넌트 함수 내에서 다른 함수의 인자로 넘기거나 자식 컴포넌트의 prop으로 넘길 때 예상치 못한 성능 문제를 막을 수 있음!

profile
oneThing

0개의 댓글