기본적인 useMemo와 useCallback에 대한 사용법을 알기 전에 Memoization부터 보는 것이 좋겠다.
Memoization이란 기존에 수행한 연산의 결과값을 어딘가에 저장해두고 동일한 입력이 들어오면 재활용하는 프로그래민 기법을 말한다. Memoization을 적절히 적용하면 중복 연산을 피할 수 있기 때문에 메모리를 조금 더 쓰더라도 성능을 최적화할 수 있다.
useMemo는 Memoization된 값을 반환한다.
import
import React, { useMemo } from "react";
사용법
useMemo(() => fn, deps)
useMomo는 deps가 변할 때, ()=>fn 이라는 함수를 실행하고, 그 함수의 반환 값을 반환한다.
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에서 deps는 [ex]이며, ex가 변화 할 때만 ()=>{console.log(ex)}
이 실행 된다.
따라서 X 버튼을 누를 때에만 콘솔창에 ex 값이 출력된다.
Y 버튼을 누르더라도 APP 이라는 함수 컴포넌트가 리렌더링 되지만, ex 라는 값은 변하지 않았기 때문에 useMemo는 실행되지 않는다.
useCallback은 Memoization된 함수를 반환한다.
import
import React, { useCallback } from "react";
사용법
useCallback(fn, deps)
useCallback 는 deps 가 변한다면, fn 이라는 새로운 함수를 반환한다.(첫번째 인자로 넘어온 함수를, 두번째 인자로 넘어온 배열 내의 값이 변경될 때까지 저장해놓고 재사용)
useCallback()을 사용하면, 해당 컴포넌트가 랜더링되더라도 그 함수가 의존하는 값들이 바뀌지 않는 한 기존 함수를 계속해서 반환한다.
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 은 다음의 순서로 진행될 것이다.
버튼 Y를 눌러도 useCallback()은 실행되지 않다가 X버튼을 눌렀을 때, 작동이 되며 함수 컴포넌트와 상태값(why)을 공유한다.
따라서 useCallback 은 함수와는 상관 없는 상태값이 변할 때, 함수 컴포넌트에서 불필요하게 함수를 업데이트 하는 것을 방지해준다.
useMemo와 useCallback의 사용법이 비슷하다고 느낄텐데 react 공식 문서에서도 인정하듯이, 아래의 두 식은 같다.
useMemo((...)=>fn, deps) === useCallback(fn, deps)
useMemo hook 함수를 납용하면, 컴포넌트의 복잡도가 올라가기 때문에 코드를 읽기도 어려워지고 유지보수성도 떨어지게 된다. 또한 useMemo가 적용된 레퍼런스는 재활용을 위해서 가바지 컬렉션(garbage collection)에서 제외되기 때문에 메모리를 더 쓰게 되어 실제 프로젝트에서 많이 사용되지 않는다고 한다.
출처 react공식문서, DaleSeo, Basemenks