프로젝트를 보다가 useCallback
, useMemo
, React.memo
를 봤는데 정확한 개념을 이해하지 못해서 찾아보기로 했는데 먼저 메모이제이션(memoization)에 대해 알아야 한다
memoization이란 기존에 수행한 연산의 결과 값을 어딘가에 저장해두고 동일한 입력이 들어오면 재활용하는 기법으로 중복 연산을 피할 수 있다는 장점이 있다
useMemo
의 memo도 이 memoization의 앞글자를 따왔다
Returns a memoized callback.
메모이제이션된 함수를 반환하는 훅이다
죽, 실행할지 말지는 본인에게 달렸다는 말이다
기본적인 구조는 다음과 같다
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>
</>
);
}
위의 코드에서 ex
가 변하면 console.log(why)
가 반환되고 이를 useCallbackReturn
에 담아 실행했기 때문에 console.log(why)
가 실행된다
여기서 중요한 것은 '새로운 함수를 반환'한다는 점인데 따라서 deps
가 변하면 함수의 형태가 동일하더라도 새로운 함수를 반환한다
const func1 = () => {};
const func2 = () => {};
console.log(func1 === func2); // false
Returns a memoized value.
메모이제이션된 값을 반환한다
기본구조는 다음과 같다
useMemo(() => fn, deps)
마찬가지로 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
버튼을 누르면 () => console.log(ex)
가 실행되어 콘솔에 ex
가 출력된다
⚠
useMemo
는 렌더링 중에 실행되므로 렌더링 중에는 하지 않는 것을 여기에 넣어서는 안된다
useCallback(fn, deps)
is equivalent touseMemo(() => fn, deps)
.
리엑트 공식문서에서도 useCallback(fn, deps)
과 useMemo(() => fn, deps)
이 같다고 말한다
그렇다면 왜 두 개의 훅으로 구분해서 만들어 놓은 걸까?
useMemo
은 함수의 연산량이 많을 때 이전과 값이 같으면 이 연산을 건너뛰기 위한 목적이다
예를 들어 useMemo(() => veryHeavyFunc(a, b), [a, b])
같이 매우 무겁고 느린 함수에서 a
, b
의 값이 바뀌지 않았다면 이 연산을 건너뛸 수 있다
useCallback
은 함수가 재생성 되는 것을 방지하기 위한 목적이다
이 훅은 useMemo
를 기반으로 만들어졌기 때문에 아무래도 비슷해보일 수 밖에 없는 것 뿐이다
React.memo
와 useMemo
의 차이점은 뭘까?
찾아보기 전에 이 둘은 같은 것이라고 나는 생각했다
하지만 근본적으로 다른데 useMemo
는 훅이기 때문에 함수형 컴포넌트에서만 사용할 수 있지만 React.memo
는 HOC이기 때문에 클래스형, 함수형 두 곳에서 다 사용할 수 있다
컴포넌트가 동일한 props
로 동일한 결과를 렌더링한다면 React.memo(component)
형태로 메모이징(memoizing)하여 성능향상을 할 수 있다
React.memo
는 이전 컴포넌트와 비교하지만 비교방식이 shallow equal
이기 때문에 이를 수정하고 싶다면 두번째 arg로 비교함수를 직접 제공할 수도 있다
하지만 이는 예측불가능한 오류를 발생할 수 있으므로 권장되지는 않는다
React.memo
는 HOC이기 때문에 함수형, 클래스형 컴포넌트에 모두 사용될 수 있다
useMemo
와 useCallback
의 가장 큰 차이점을 참조 동일성을 갖는 '값'을 계산하느냐 '함수'를 계산하느냐의 차이이다