IT/React

useCallback 과 useMemo 의 차이

라임웨일 2022. 6. 28. 16:30
반응형

메모이제이션(memoization)이란?

useMemo 함수에 대해서 알아보기 전에 알고리즘 시간에 자주 나오는 메모이제이션(memoization) 개념에 대해서 잠깐 알아보겠습니다. memoization이란 기존에 수행한 연산의 결괏값을 어딘가에 저장해두고 동일한 입력이 들어오면 재활용하는 프로그래밍 기법을 말합니다. memoization을 잘 적용하면 중복 연산을 피할 수 있기 때문에 메모리를 조금 더 쓰더라도 애플리케이션의 성능을 최적화할 수 있습니다.

useMemo와 useCallback을 너무나도 어렵게 설명하는 곳이 많아서 여기서는 간단하게만 알아보겠습니다.

1. useMemo

메모이제이션된 ''을 반환한다.

용법

useMemo(() => fn, deps)

useMemo는 deps 가 변한다면, () => fn이라는 함수를 실행하고, 그 함수의 반환 값을 반환합니다. deps는 dependency이며, useMemo가 이 deps라는 것에 '의존'한다는 뜻입니다. 

예시

import React, { useState, useCallback, useMemo } from "react";

export default function App() {
  const [ex, setEx] = useState(0);
  const [why, setWhy] = useState(0);

  // useMemo 사용하기
  useMemo(() => {console.log(ex)}, [ex]);

  // 두 개의 버튼을 설정했다. X버튼만이 ex를 변화시킨다.
  return (
    <>
      <button onClick={() => setEx((curr) => (curr + 1))}>X</button>
      <button onClick={() => setWhy((curr2) => (curr2 + 1))}>Y</button>
    </>
  );
}

위와 같은 버튼이 있습니다.  먼저 위의 코드에서 X라는 버튼을 클릭할 때 때 'ex'라는 상태값이 변화하는 코드입니다. 

useMemo(() => {console.log(ex)}, [ex]) 

위의 코드에서 deps는 [ex]입니다. 즉 ex 가 변할 때에만 () => {console.log(ex)} 이 실행됩니다. 따라서 X 버튼을 누를 때에만 콘솔창에 ex 값이 출력됩니다.. Y 버튼을 누르더라도 APP이라는 함수 컴포넌트가 전부 재실행(리렌더링) 되지만, ex라는 값은 변하지 않았기 때문에 useMemo는 에는 아무런 변화가 없습니다.


2. useCallback

메모이제이션된 '함수'를 반환한다.

 

 useMemo는 함수를 실행해버리는데, useCallback은 함수를 반환합니다.

useCallback(fn, deps)

useCallback 은 deps 가 변한다면, fn이라는 새로운! 함수를 반환한다.

import React, { useState, useCallback, useMemo } from "react";

export default function App() {
  const [ex, setEx] = useState(0);
  const [why, setWhy] = useState(0);

  // useCallback 이 () => {console.log(why)} 라는 함수를 반환한다.
  const useCallbackReturn = useCallback(() => {console.log(why)}, [ex]);

  // useCallback 이 담겨있는 함수를 실행
  useCallbackReturn()

  return (
    <>
      <button onClick={() => setEx((curr) => (curr + 1))}>X</button>
      <button onClick={() => setWhy((curr2) => (curr2 + 1))}>Y</button>
    </>
  );
}

처음 예시와 같은 버튼을 클릭할 때 이벤트가 발생되는 코드입니다. 위의 useCallback 은 () => {console.log(why)}라는 함수를 반환해주고 있습니다. 위의 useCallback 은 다음의 순서로 진행됩니다.

  1. 처음 컴포넌트가 시작될 때 실행 () => {console.log(0)}
  2. ex 가 변할 때까지 함수는 () => {console.log(0)}
  3. ex 가 변한다면 그제서야 why 의 값을 가져와서 () => {console.log(새로운 값)}

 

예를 들어서 Y 버튼을 다섯번 누른다고 가정합니다. Y 버튼이 눌리면 'why'라는 상태의 값이 1씩 증가합니다.

 

 

위의 움짤에서 보듯이, Y를 다섯번 누를 때 동안에는 계속 함수가 () => {console.log(0)}입니다.

(물론 이 때 why라는 상태값은 계속 값이 증가합니다.) 그러다가 X 버튼을 누르면서 ex라는 변수(deps)가 변하자마자, () => {console.log(5)} 를 반환됩니다. deps가 변해야 함수 컴포넌트와 상태 값(why)을 공유하는 것입니다.

 

따라서 useCallback 은 함수와는 상관없는 상태 값이 변할 때, 함수 컴포넌트에서 불필요하게 함수를 업데이트하는 것을 방지해줍니다. 다만, deps를 잘못 설정하면 아무리 함수 컴포넌트를 재실행해도 함수가 변하지 않으면서 내가 원치 않는 상황이 올 수도 있으므로 섬세한 컨트롤이 필요합니다. 이런 상황은 useMemo도 똑같습니다. useMemo 역시 Y 버튼을 5번 눌러도, X 버튼을 안 누르면 실행이 되지 않습니다.

 

추가로, react 공식 문서에서도 인정하듯이, 아래의 두 식은 같습니다.

useMemo((...)=> fn, deps) === useCallback(fn, deps)

3. useCallback의 새로고침

useCallback 은 deps 가 변하면서 함수를 반환할 때, 형태가 같더라도 아예 새로운 함수를 반환합니다.

const add1 = () => {};
const add2 = () => {};

 

 

위 두 함수는 같은가? 에 대한 대답은 두 함수는 전혀 같지 않습니다.

 

각 변수는 같은 함수를 바라볼 뿐 전혀 다른 변수입니다. 바라보는 값만 같을 뿐, 전혀 다른 메모리를 가진 변수입니다.


useCallback 은  새로운 함수를 반환합니다.

const useCallbackReturn = useCallback(() => {}, [ex])

여기서 ex 가 변할 때, useCallback 은 새로운 함수를 반환합니다.. 그 말은 즉, ex == 0 일 때와 ex == 1 일 때의 () => {} 은 다른 함수입니다. 새로운 무기명 함수를 반환했기 때문인데 이는 앞서 기술했듯이, 값이 같을 뿐 다른 메모리입니다.

출처 : https://basemenks.tistory.com/238

반응형