useMemo는 특정 값(value)를 재사용하고자 할 때 사용하는 hook이다.
function Calculator({value
const result = calculate(value);
return <>
<div>
{result}
</div>
</>;
}
해당 컴포넌트는 props
로 넘어온 value
값을 calculator
라는 함수에 인자로 넘겨서 result
값을 구한 후, <div>
엘리먼트로 출력하고 있습니다.
만약 여기서 calculator
가 내부적으로 복잡한 연산을 해야 하는 함수라 계산된 값을 반환하는데에 시간이 몇 초 이상 걸린다고 가정했을 때, 해당 컴포넌트는 렌더링을 할 때마다 이 함수를 계속 호출 할 것이고, 이는 사용자에게 로딩 속도가 느리다는 인식을 줄 수 있습니다.
/* useMemo를 사용하기 전에는 꼭 import해서 불러와야 합니다. */
import { useMemo } from "react";
function Calculator({value}){
const result = useMemo(() => calculate(value), [value]);
return <>
<div>
{result}
</div>
</>;
}
value
는 일종의 값으로서, 이 값이 계속 바뀌는 경우라면 어쩔 수 없겠지만, 렌더링을 할 때마다 이 value
값이 계속 바뀌는 게 아니라고 생각해 봅시다.
그럼 이 값을 어딘가에 저장을 해뒀다가 다시 꺼내서 쓸 수만 있다면 굳이 calculate 함수를 호출할 필요도 없을 것입니다.
여기서 useMemo Hook
을 사용할 수 있습니다.
이런 식으로
useMemo
를 호출하여calculate
를 감싸주면, 이전에 구축된 렌더링과 새로이 구축되는 렌더링을 비교해value
값이 동일할 경우에는 이전 렌더링의value
값을 그대로 재활용할 수 있게 됩니다.
이는 메모이제이션(Memoization) 개념과 긴밀한 관계가 있습니다.
메모이제이션(Memoization)은 알고리즘에서 자주 나오는 개념이다.
기존에 수행한 연산의 결과값을 메모리에 저장하고, 동일한 입력이 들어오면 재활용하는 프로그래밍 기법을 말한다.
이 메모이제이션을 적절히 사용한다면 굳이 중복 연산을 할 필요가 없기 때문에 앱의 성능을 최적화 할 수 있다.
useMemo
는 바로 이 개념을 이용하여 복잡한 연산의 중복을 피하고 React 앱의 성능을 최적화 시킨다.
useCallback 또한 useMemo와 마찬가지로 메모이제이션 기법을 이용한 hook입니다.
useMemo는 값의 재사용을 위해 사용하는 hook이라면, useCallback은 함수의 재사용을 위해 사용하는 hook입니다.
function Calculator({x, y
const add = () => x + y
return <>
<div>
{add()}
</div>
</>;
}
이 함수는 해당 컴포넌트가 랜더링 될 때마다 새롭게 만들어진다.
useMemo
와 마찬가지로, 해당 컴포넌트가 리랜더링 되더라도 그 함수가 의존하고 있는 값인 x와 y가 바뀌지 않는다고 생각했을 때, 함수 또한 메모리 어딘가에 저장해 뒀다가 다시 꺼내서 쓸 수 있을 겁니다.
/* useCallback를 사용하기 전에는 꼭 import해서 불러와야 합니다. */
import React, { useCallback } from "react";
function Calculator({x, y}){
const add = useCallback(() => x + y, [x, y]);
return <>
<div>
{add()}
</div>
</>;
}
사실 useCallback
만 사용해서는 useMemo
에 비해 괄목할 만한 최적화를 기대하기 어렵다.
useCallback
은 함수를 호출 하지 않는hook
이 아니라, 그저 메모리 어딘가에 함수를 꺼내서 호출하는hook
이기 때문이다.
따라서 단순히 컴포넌트 내에서 함수를 반복해서 생성하지 않기 위해서useCallback
을 사용하는 것은 큰 의미가 없거나 오히려 손해인 경우도 있다.
그러면 언제 사용하는 게 좋을까?
자식 컴포넌트의 props
로 함수를 전달해줄 때 이 useCallback
을 사용하기 좋다.
useCallback은 참조 동등성에 의존한다.
React는 JavaScript 언어로 만들어진 오픈소스 라이브러리기 때문에 기본적으로 JavaScript의 문법을 따라간다.
자바스크립트에서 함수는 객체이다. 객체는 메모리에 저장할 때 값을 저장하는게 아니라 값의 주소를 저장하기 때문에, 변환하는 값이 같을 지라도 일치연산자로 비교했을 때 false
가 출력된다.
function doubleFactory(){
return (a) => 2 * a;
}
const double1 = doubleFactory();
const double2 = doubleFactory();
double1(8); // 16
double2(8); // 16
double1 === double2; // false
double1 === double1; // true
double1
과 double2
는 같은 함수를 할당했음에도 메모리 주소 값이 다르기 때문에 같다고 보지 않는다.
따라서 두개의 함수는 동일한 코드를 공유하더라도 메모리 주소가 다르기 때문에, 메모리 주소에 의한 참조 비교 시 다른 함수로 본다.
React는 리랜더링 시 함수를 새로이 만들어서 호출을 한다.
새로이 만들어 호출된 함수는 기존의 함수와 같은 함수가 아니다. 그러나
useCallback
을 이용해 함수 자체를 저장해서 다시 사용하면 함수의 메모리 주소 값을 저장했다가 다시 사용한다는 것과 같다고 볼 수 있다.
따라서 React 컴포넌트 함수 내에 다른 함수의 인자로 넘기거나 자식 컴포넌트의 props으로 넘길 때 예상치 못한 성능 문제를 막을 수 있다.