프론트엔드/React

[React] useCallback 사용한 성능 최적화

s_omi 2024. 1. 29. 12:32

useCallback 

React Hooks 중 하나로서 useMemo와 같이 Memoization 기법으로 컴포넌트 성능을 최적화시키는 방법이다.

 

useMemo나 memoization을 모른다면 아래의 링크를 통해 꼭 보고 오자!

 

[React] Memoization과 useMemo 사용법

useMemo React Hooks 중에 하나로서 컴포넌트 성능 최적화를 위해 사용한다. 성능 최적화를 위해 사용하는 Hooks에는 useCallback도 있다. [React] useCallback 사용한 성능 최적화 useCallback React Hooks 중 하나로서

mi-dairy.tistory.com

useCallback는 첫 번째 인자로 들어간 "콜백함수 그 자체"를 memoization 한다. 즉 콜백함수를 메모리에 저장한다는 말이다. 

 

 

사용법

useCallback은 첫 번째 인자로는 콜백함수, 두 번째 인자로는 의존성 배열을 받아 총 두 개의 인자를 받는다.

const add = useCallback((num) => {
  return num + 1;
}, [value]);

 

첫 번째 인자는 Memoization할 콜백 함수이다.


두 번째 인자인 의존성 배열은 배열 안의 요소가 업데이트될 때마다 useCallback의 콜백 함수를 재실행시킨다. 콜백 함수를 재실행시킴으로써 메모리에 저장된 함수는 업데이트되어 메모리에 새로 저장된다. 

빈 배열일 경우 제일 처음 마운트될 때만 함수를 메모리에 저장하고 그 이후엔 메모리에 있는 함수를 재사용한다.

 

 

사용 이유 

function App() {
  const add = (num) => {
    return num + 1;
  };
    
  return <div>{value}</div>
}

 

함수형 컴포넌트인 App이 렌더링되면 내부에 있는 모든 변수 및 함수가 다시 실행되어 초기화되게 된다. 그러면 내부 함수인 add은 App 컴포넌트가 렌더링될 때마다 계속 새로 호출되어야 하고 이는 불필요한 호출을 야기한다. 

 

const add = useCallback((num) => {
  return num + 1;
}, [value]);

 

그렇기 때문에 이때 useCallback을 사용하게 되면 useCallback은 콜백함수를 메모리에 저장하므로 App 컴포넌트가 렌더링되어 add가 호출되어도 이미 메모리에 저장된 함수를 재사용하기 때문에 불필요한 호출을 줄일 수 있다.

즉, 컴포넌트가 처음 렌더링될 때만 add 변수를 초기화해주고 이후에 렌더링될 때는 이미 이전에 할당받은 함수 객체를 재사용하게 된다.

 

 

사용 예시

함수 객체는 렌더링 될 때마다 함수 객체가 초기화되고 새로 만들어져서 새로운 주소에 할당되기 때문에 useEffect 입장에서는 의존성 배열에 함수를 넣으면 렌더링 될 때마다 새로운 함수 객체이므로 useEffect의 콜백함수를 호출하게 된다. 


이럴 때 이 함수 객체를 useCallback 처리하면 이전에 memoization된 함수 객체의 주소를 가지고 있기 때문에 리렌더링 되었을 때 useEffect의 콜백함수를 호출하지 않게 된다.

function App() {
  const [num, setNum] = useState(0);
    
  const tempFunc = useCallback(() => {
    console.log(`num :: ${num}`);
    return;
  }, []);
    
  useEffect(() => {}, [tempFunc]);
    
  return (
    <div>
      <input type="number" value={num} onChange={(e) => setNum(e.target.value)} />
      <button onClick={tempFunc}>확인</button>
    </div>
  );
}

 

다음과 같은 코드를 실행시켰을 때 num의 값을 아무리 올려도  확인  버튼을 누르면 console.log 로 찍히는 num의 값이 0인 것을 확인할 수 있다. 코드를 자세히 보면 useCallback의 두 번째 인자가 빈 배열인 것을 알 수 있다. 빈 배열이기 때문에 useCallback은 처음 마운트될 때만 실행되고 그때의 콜백함수 자체를 메모리에 저장했을 것이다. 때문에 처음 마운트되었을 때의 num 값인 0 그대로 저장되어 버튼을 눌렀을 때 num 값이 변했지만 0이 출력된 것이다. 

 

이때 num 값이 변할 때마다 useCallback의 콜백함수에 업데이트를 하고 싶다면 다음과 같이 수정하면 된다. 

const tempFunc = useCallback(() => {
  console.log(`num :: ${num}`);
  return;
}, [num]);

 

useCallback 내의 변수를 사용했을 때 의존성 배열에 그 변수를 넣지 않게 되면 리렌더링되었을 때 실행되지 않아 변수가 새로운 값으로 변하지 않고 이전(처음)에 메모리제이션된 값으로 유지하게 되기 때문에 내부에 변수가 있다면 의존성 배열에 넣어야 한다.

 

 

 


출처 : 유튜브 별코딩 (직접 듣기 강추!)