12. React와 Memoization

Memoization

컴퓨터가 동일한 계산을 반복해야할 때, 이전에 계산한 값을 메모리에 저장하여 반복 수행을 줄이고 프로그램 실생 속도를 빠르게 하는 기술.

알고리즘에서 memoization

주로 알고리즘에서 동적 계획법 문제를 풀 때 자주 사용되는 구조이며, 이와 반대되는 개념으론 Tabulation(Bottom up) 이 있음. 전반적인 구조는 다음과 같이 이루어짐.

  1. 재귀되는 코드 작성
  2. 리턴 값을 기억하고 있다가 재귀 호출에 사용됨.

JS에서 memoization

  1. closure
  2. 고차함수(함수가 함수를 리턴하는 경우)

useMemo

원치 않는 상황에서까지 컴포넌트가 리렌더링될 때 자원의 낭비를 막기 위해 사용
이 때 성능 최적화를 위해 사용되는 것이 useMemo.

import { useMemo, useState } from "react"
import "./App.css"

function App() {
  const example = (users) => {
    console.log("example 함수 실행")
    return users.filter((user) => user).length
  }

  const [users, setUsers] = useState([false, false, false])
  const [ex, setEx] = useState(false)
  const handleClick = (i) => {
    users[i] = !users[i]
    setUsers([...users])
  }

  const handleInput = () => {
    setEx(!ex)
  }
  // const check = example(users)
  const check = useMemo(() => example(users), [users])
  return (
    <>
      <h1>check circle +{check}</h1>
      <input onInput={handleInput} type="text" />
      {users.map((e, i) => {
        return e ? (
          <div key={i} onClick={() => handleClick(i)} className="red circle" />
        ) : (
          <div key={i} onClick={() => handleClick(i)} className="blue circle" />
        )
      })}
    </>
  )
}

export default App

만약 useMemo를 하지 않은 상태에서 다음 코드를 실행하면 user의 갯수가 늘어나거나 줄어나는 상황과 관계 없는 상황에서도 count 함수가 실행되는 것을 확인 할 수 있음

useMemo(어떻게 연산할지 정의하는 함수, 의존성 배열(deps 배열))
ex) const check = useMemo(() => example(users), [users])

useMemo를 사용하면 의존성배열에 작성한 변수가 바뀌었을 때만 함수가 동작하고, 다른 이유로 재렌더링된다면 동작하지 않음.

useCallback

useMemo와 비슷한 훅으로 useMemo가 결과값을 저장해 재사용한다면, useCallback은 함수를 재사용할 때 사용함. 함수들은 컴포넌트가 리렌더링 될 때마다 새로 만들어짐.

함수에서 사용되는 Props가 있다면 반드시 deps 배열안에 포함시켜야 함.

useCallback 사용x

useCallback 사용

렌더링시 둘의 차이가 뚜렷하게 나타나지는 않음. 하지만 이렇게 함수를 최적화시켜야, props가 바뀌지 않았을 때 virtualDOM에 재렌더링이 되지 않도록 할 수 있음 -> React.memo 사용

useEffect vs useMemo vs useCallback

useEffect와 비교했을 때 페이지가 처음 렌더링되는 시점에서 차이가 발생할 줄 알았으나, 실제로는 두 함수 모두 처음 렌더링되는 시점에서 실행되는 것처럼 보임.

useMemo는 useEffect와 비슷하지만, 실행되는 시점이 다르다

useMemo는 local call로 이 컴포넌트로만 뭔가를 할 수 있음. useEffect는 collective call로 비동기던 아니던 모든 컴포넌트의 렌더링이 모두 끝난 후 이루어짐.
useMemo -> useLayoutEffect -> useEffect의 순으로 실행됨.

값을 저장해두고 반복실행을 막아준다.

Memoization 을 위해 useMemo는 리턴값을 저장해두고 필요할 때마다 꺼내서 사용함.
useCallback에서는 함수를 저장해두고 사용. useEffect에서는 최적화가 이뤄지지 않는다.

React.memo

React.memo
UI성능을 증진시키기 위해 React는 고차 컴포넌트(HOC) React.memo()를 제공. React.memo()로 래핑된 렌더링 결과를 저장해 다음 렌더링시 Props가 같다면 메모이징된 내용을 재사용해 불필요한 재렌더링을 막음. 함수형 컴포넌트에 적용됨.

props가 같은지 확인하는 방법

props 혹은 props의 객체를 비교할 때 얕은 비교를 실행.
비교방식을 변경하고 싶다면 React.memo의 두번째 매개변수로 비교함수를 만들어서 넘겨주면 됨.
비교함수가 true를 반환한다면 두 객체가 같은 객체라고 판단함.
React.memo(component, [equalFuc(a, b)])

React.memo()를 권장할 때

같은 props로 렌더링을 자주 일어나는 컴포넌트

부모컴포넌트가 자주 재렌더링될 때, 자식 컴포넌트에 변함이 없는 경우 재렌더링이 일어나는 걸 막기 위해 자식 컴포넌트에 React.memo를 사용하는 것을 권장

React.memo()를 권장하지 않을 때

위의 권장하는 상황이 아니라면 가급적 사용을 권장하지 않음.

클래스 기반의 컴포넌트인 경우

클래스 기반의 컴포넌트에서 최적화가 필요하다면, PureComponent를 확장해 사용하거나 shouldComponentUpdate()메서드를 구현하는 걸 추천

Props가 자주 바뀌는 경우

props가 동일할 때 메모리 최적화가 발생하기 때문에 props가 자주 변경된다면 굳이 사용할 이유가 없음.

props로 함수를 넘겨주는 경우

함수는 일반객체와 동일한 비교원칙을 따라 오직 자신과 자신을 비교했을 때만 동일 (true)

그래서 이때 사용되는게 useCallback()
useCallback()을 이용해 함수 인스턴스를 보존시켜 같은 함수가 반환될 수 있도록 함.

좋은 웹페이지 즐겨찾기