이번에는 최적화 고민을 한 번이라도 해봤다면 들어봤을 uesMemo와 useCallback을 정리해볼까 한다. 찾아보다 배운 게 (참고 링크),
내가 진행했던 프로젝트들에서는 그렇게 큰 연산이 없었고, 주로 useState를 실행할 때 상관 없는 component가 리렌더링 되는 문제 때문에 최적화를 고민했던 거였어서,
결국 useMemo나 useCallback보다는 React.memo
를 써야함을 알았다... 😥 그래도 우선 짚고 넘어갈 건 짚고 가자!
useMemo
는 계산 비용이 많이 드는 함수의 반환값을 메모이제이션하는 역할을 수행한다.
메모이제이션(Memoization)은 이전에 계산한 결과를 저장하고, 동일한 입력값이 들어왔을 때 이전에 계산한 결과를 반환하는 것이다.
예를 들어,
function add(a, b) {
return a + b;
}
위와 같이 두 인자의 덧셈을 반환하는 함수가 있다고 했을 때, add(1, 2)를 호출하면 3이 반환된다. 그리고 이후에 add(1, 2)를 다시 호출하면, 이전에 계산한 결과를 새로 계산하지 않고 바로 반환하는 것이다.
const memoizedValue = useMemo(() =>
computeExpensiveValue(a, b), [a, b]
);
computeExpensiveValue(a, b)
해당 함수가 반환하는 값을 메모이제이션 하고,
두번째 [a, b]
부분은 매개변수로서, 이 배열의 요소들이 변경되었을 때만 메모이제이션 된 값이 업데이트 된다.
하지만 단순히 a+b를 메모이제이션 한다든가, 혹은 initial state를 메모이제이션 한다는 것은 useMemo를 사용하고 난 뒤의 성능이 오히려 떨어질 수 있다.
useCallback
은 메모이제이션된 함수를 반환한다.
const memoizedCallback = useCallback(() => {
doSomething(a, b);
},
[a, b],
);
useCallback(fn, deps)은 useMemo(() => fn, deps)와 같다.
useCallback은 새로운 콜백 함수를 생성하지 않는 것에 초점을 맞추고, useMemo는 함수의 계산 비용이 많이 들 경우에 최적화를 목적으로 하고있다.
즉,
useMemo
는 반환되는 값에 대한 메모이제이션을 목적으로 사용되고,useCallback
은 함수 자체를 메모이제이션하기 위해 사용된다.
몇 번이나 적었지만, 결국 useMemo와 useCallback 둘 다 비용이 많이 드는 함수를 메모이제이션할 때 성능최적화를 이룰 수 있다. 단순 연산 등에 대해서는 피해야 한다.
내가 작성한 함수가 expensive한지, 아닌지를 모르겠다면 비용계산을 통해 useMemo와 useCallback의 사용이 적합한지 확인해보고 사용해야 한다.