본문 바로가기

React

[React] 최적화 훅 useMemo, useCallback

React는 state가 변경되면 리렌더링을 한다. 다시말해 Virtual DOM과 DOM을 비교하여 변경된 부분을 반영하고 다시 렌더링 해주는 과정을 거친다.

이러한 과정에서 비효율적인 문제가 존재한다. 무조건 state가 변경되면 변경된 state와 무관한 컴포넌트의 구성요소가 리렌더링이 된다는 것이다.

이렇게 되면 불필요한 연산을 하는 것은 물론이고 연산을 다시하는 과정을 거쳐서 결과적으로 성능 저하로 좋지 않은 사용자 경험을 제공한다.

그걸 극복하기 위해서 나온 훅들이 useMemouseCallback이다.

useMemo

const Example = () => {
    const [textInput, setTextInput] = useState('');
    const [count, setCount] = useState('카운트');

    const onCount = () =>{
        console.log('카운트..');
        return count
    }

    const outcount = onCount()

    return (
        <div>
            <input type="text" onChange={(e) => { setTextInput(e.target.value) }} value={textInput} />
            <p>{outcount}</p>
        </div>
    )
}
export default Example;

 

첫 렌더링

<p>{count}</p>대로 "카운트" 렌더링이 된 것을 볼 수 있다.

여기서 문제는 현재 p 태그와 연결된 변수와는 아무런 관계가 없는 input 태그의 값을 변경하면 함수가 호출되어 실행되는 것을 볼 수 있다.

&nbsp;input 입력

input 태그에 값을 입력하자 console에는 카운트가 찍힌다. input 태그와 onCount 함수는 아무런 관련이 없다.

그렇다면 어째서 이런 현상이 일어나는가.

그건 바로 state가 변경되면 리렌더링이 일어나는 리액트에 특징 때문이다.

이런 상황에서 사용하는 것이 useMemo Hook이다.

 

 

const Example = () => {
    const [textInput, setTextInput] = useState('');
    const [count, setCount] = useState('카운트');

    const onCount = () =>{
        console.log('카운트..');
        return count
    }

    const outcount = useMemo(() => onCount(), [count])

    return (
        <div>
            <input type="text" onChange={(e) => { setTextInput(e.target.value) }} value={textInput} />
            <p>{outcount}</p>
        </div>
    )
}
export default Example;

 

 

첫 코드와 달라진 점은 outcount 변수를 useMemo를 사용한 함수를 대입해주었다.

useMemo는 인자를 두개를 입력해야하는데

  • 정의할 함수
  • deps 배열을 넣어주면 되는데, 이 배열 안에 넣은 내용이 바뀌면, 등록한 함수를 호출해서 값을 연산해주고, 만약에 내용이 바뀌지 않았다면 이전에 연산한 값을 재사용하게 된다.

결과는 성공적으로 input을 아무리 변경해도 리렌더링이 일어나지 않게되었다.

useMemo 사용

useCallback

useCallback은 useMemo와 같이 최적화를 위한 Hook이고 용법이 크게 다르지않다.

차이점이라면 useMemo는 값에 리렌더링을 막기 위해서 메모이제이션된 값을 반환하는 것이고 useCallback은 useMemo와 동일하게 리렌더링을 방지하기 위해 메모이제이션된 콜백 함수를 반환하는 것이다.

 

 

const Component = () => {
  const [count, setCount] = React.useState(0)
  const handleClick = () => console.log('clicked!')

  return (
    <>
      <button onClick={() => setCount(count + 1)}>카운트 올리기</button>
      <button onClick={handleClick}>클릭해보세요!</button>
    </>
  )
}

 

 

useMemo를 사용한 이유는 컴포넌트에 state 중 하나라도 바뀌면 다른 함수들도 같이 리렌더링 된다는 것 때문에 사용하는 것이다.

useCallback 또한 다르지 않다. 컴포넌트가 렌더링 될 때 마다 콜백함수를 새로 생성한다는 단점이 있고. 부모 컴포넌트가 렌더링되거나, 상태(state)가 변경되는 경우, React 컴포넌트는 리렌더링을 한다. 다음은 useCallback을 사용한 코드이다.

 

 

const Component = () => {
  const [count, setCount] = React.useState(0)
  const handleClick = React.useCallback(
    () => console.log('clicked!'),
  [count]) // useCallback 사용

  return (
    <>
      <button onClick={() => setCount(count + 1)}>카운트 올리기</button>
      <button onClick={handleClick}>클릭해보세요!</button>
    </>
  )
}