프레임워크/React

[React] useCallback

은돌1113 2022. 2. 18. 11:43

useCallback도 useMemo와 마찬가지로 메모 이제이 션 기법을 사용하여

컴포넌트의 성능 최적화 해주는 React Hook입니다.

 

메모지 에이션이란?

이전에 이미 계산한 값을 캐싱해둠으로써 해당 값이 필요할 때마다

반복적으로 계산하는 게 아니라 메모리에서 꺼내서 재사용하는 최적화 기법을 말합니다.


useMemo란?

첫 번째 인자로 들어온 Callback 함수에서 return 하는 값을 메모 이제이 션 해두었다가 재사용 하는 기법입니다.

 

useCallback이란?

useMemo와 마찬가지고 첫번째 인자로 들어온 Callback 함수를 재사용 하지만,

useMemo와 다르게 callback 함수 자체를 메모이제이션 해두었다가 재사용하는 기법입니다.

 

React에서 함수형 컴포넌트는 함수 자체이기 때문에

렌더링이 일어나는 경우 컴포넌트 함수가 호출되면서 모든 내부 변수가 초기화되는 데요.

위와 같은 코드 형태에서는 Component라는 함수가 호출될 때마다

내부에 있는 calculate 함수도 매번 초기화될 것입니다.

 

이때 useCallback을 사용하여 메모 이제이 션 해준다면?

컴포넌트가 다시 렌더링 되더라도 calculate 함수는 초기화되지 않기 때문에

메모리에 저장된 기존 calculate 함수를 사용하여 불필요한 용량을 줄일 수 있습니다.

 

즉, 컴포넌트가 맨처럼 렌더링 될 때 calculate 함수를 메모리에 초기화하여 저장하고,

두 번째 인자에 있는 dependency array의 요소의 값이 바뀌면

다시 한번 더 calculate 함수를 호출하여 메모리에 저장한다는 것입니다.


useCallback 구조

useCallback은 두 개의 인자를 받습니다.

첫 번째 인자로는 메모 이제이 션 해줄 callback 함수

두 번째 인자로는 의존성 배열(dependency array)을 받습니다.

(의존성 배열 안에 있는 요소의 값이 바뀔 때만

첫 번째 인자에 있는 callback 함수를 다시 초기화하여 메모리에 저장해줍니다.)


실습

1️⃣ 예제를 하나 만들고, useEffect에 someFunction을 의존성 배열(dependency array)에 넣어 주었습니다.

그 후 input 태그의 숫자를 증가시켜 주었습니다.

콘솔에서 확인해볼 수 있듯이 number state의 값이 바뀔 때마다 App 컴포넌트가 렌더링 되고,

App 컴포넌트 내부의 someFunction 함수의 주소 값이 바뀌었기 때문에

useEffect 입장에서는 someFunction이 바뀌었다고 인지하여 콘솔이 찍히는 걸 볼 수 있습니다.

더보기
import React, { useState, useEffect } from "react";

function App() {

  const [number, setNumber] = useState(0);

  const someFunction = () => {
    console.log(`someFunction : number : ${number}`);
    return;
  }

  useEffect(() => {
    console.log("someFunction이 변경 되었습니다.")
  }, [someFunction])

  return (
    <div>
      <input
        type="number"
        value={number}
        onChange={(e) => setNumber(e.target.value)}
      />
      <br />
      <button onClick={someFunction}>Call someFunc</button>
    </div>
  );
}

export default App;

 

2️⃣ useCallback을 사용한다면 어떻게 다를까요?

 

1번 코드에서 useCallback을 추가하고, 실행해보았을 때

아래와 같이 number state가 바뀌면서 App 컴포넌트가 렌더링 되어도

someFunction은 초기화되지 않는 것을 볼 수 있습니다.

그렇다면 Call someFunc 버튼을 눌렀을 때는 number가 잘 찍힐까요?

위와 같이 number state가 변하였음에도 불구하고, someFunction 함수에서는 변하지 않은 것을 볼 수 있습니다.

왜냐하면 useCallback을 사용하여 someFunction을 메모 이제이 션 해주었을 때는 number가 0인 상태였고,

우리는 메모이제이션된 someFunction만 가져오는 것이기 때문에 그 상태 그대로 남아 있는 것입니다.

 

3️⃣ number state가 업데이트될 때마다 메모이제이션된 someFunction도 업데이트시켜주고 싶다면

두 번째 인자에 있는 의존성 배열(dependency array)에 number를 넣어주면 됩니다.

이렇게 해주면 number가 바뀔 때마다 useCallback이 초기화되어 원하는 값을 얻을 수 있습니다.

 

4️⃣ 지금은 다른 state가 없기 때문에 useCallback 사용 전과 후의 차이를 크게 못 느낄 수 있습니다.

아래 두 경우를 비교해보면 더 이해하기 쉬울 것 같습니다.

 

두 개의 state가 있고, 각각 toggle, number라는 이름을 가지고 있습니다.

 

🥕 useCallback 사용 안 했을 경우 

toggle의 값이 업데이트될 때마다 연관성 없는 someFunction 함수가 매번 초기화되어 콘솔이 찍힙니다.

 

🥕🥕 useCallback 사용했을 경우 

toggle이 아무리 업데이트되더라도 number가 업데이트되지 않는 한 초기화되지 않아 콘솔이 안 찍힙니다.

 

이처럼 useCallback과 useMemo를 적절히 사용하면 메모리 최적화에 용이합니다!


더 알아보기