[TIL] useCallback 와 useMemo

useMemo란?

useMemo 는 이전 값을 기억해서 성능을 최적화하는 용도로 사용되며 계산량이 많은 함수의 반환값을 재활용하는 용도로 사용된다

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b])

useMemo 의 사용방법은 첫 번째 매개변수로 함수를 입력하고 두 번째 매개변수는 의존성 배열이다 이 의존성 배열이 변경되지 않으면 이전에 반환 된 값을 재사용 하며 의존성 배열에 빈 값을 입력하면 매번 값을 새롭게 계산하여 return 한다



useCallback이란?

useCallback 은 함수를 새로 만들지 않고 재사용하고 싶을 경우 사용하게 되며 리액트 컴포넌트 안에서 생성되는 함수는 매 렌더링마다 재생성되기 때문에 불필요한 렌더링을 방지하기 위해 사용한다 즉 최적화를 위한 훅이다

함수는 객체이기 때문에 렌더링 이전과 이후의 메모리 주소가 달라지는데 useCallback을 사용하지 않은 함수들은 컴포넌트가 리렌더링 될 때마다 새로 만들어진다. 함수가 새로 만들어지는 것이 성능을 떨어뜨리지는 않지만, 컴포넌트 결과물을 재사용하거나 하는 최적화 작업을 할 때 함수를 재사용하는 것이 필요하다. 다시 말해 똑같은 모양의 함수를 계속해서 만들어 메모리에 계속해서 할당하고 있다는 것을 뜻한다.


useCallback사용방법

const memoizedCallback = useCallback(() => {
  doSomething(a, b)
}, [a, b])

useCallback인라인 콜백과 의존성 값의 배열 이렇게 2개의 인자를 받는다. 의존성 배열은 useEffect의 의존성 배열과 비슷한 개념인데 빈 배열 입력 시 렌더링 되는 최초의 한 번만 생성된다 주의할 점은, 함수 안에서 사용하는 상태 혹은 props 가 있다면 꼭, 의존성 배열 안에 포함시켜야 한다. 만약에 의존성 배열 안에 함수에서 사용하는 값을 넣지 않게 된다면, 함수 내에서 해당 값들을 참조할 때 가장 최신 값을 참조할 것이라고 보장할 수 없다. 이제 실제 사용 예제를 보자

function Component() {
  const handleClick = () => console.log('click!!!')

  return <button onClick={handleClick}>클릭</button>
}

위 코드는 보기에는 별문제가 없다 하지만 컴포넌트가 렌더링 될 때마다 함수를 새로 생성한다 즉 부모 컴포넌트가 렌더링 되거나, 상태 값이 변경되는 경우 리액트는 리렌더링을 한다

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

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

카운트를 증가하는 버튼을 클릭해서 count의 값이 변경되면 리렌더링이 되는데 이때 컴포넌트 함수가 새로 호출됨을 의미하고 매 렌더링마다 handleClick 함수를 새로 생성된다. 이런 부분에서 불필요한 메모리를 낭비하지 않고 최적화를 하기 위해 useCallback을 사용한다

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

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

handleClickuseCallback으로 감싸는 걸로 더 이상 handleClick 함수는 렌더링 될 때마다 새로 생성되지 않고 이전에 생성한 함수를 저장해두고 재사용한다 동작은 똑같지만 좀 더 최적화 부분에서 좋다.

useCallback의 두 번째 인자 값 [] 배열에 대해 알아보자

두 번째 인자는 useEffect와 같이 배열의 의존성을 의미한다 즉 두 번째 인자에 입력한 값이 변경될 때에만 useCallback이 실행이 된다는 것이다

const handleClick = useCallback(() => console.log('현재 값 :' + count), [count]) // 의존성 값

handleClick 함수에 count 값을 출력하는 코드이며 값이 변경될 때에만 handleClick 함수가 실행이 된다. 이처럼 useCallback 함수 내부에서 의존하는 상태 값이 있다면 반드시 두 번째 인자에 추가해야 한다.

useMemo()와 useCallback() 차이점

  • useMemo : '함수 return 값'을 기억
  • useCallback : '함수 reference'를 기억