[React] useCallback, Meomoization
useCallback이란?
: 컴포넌트가 리렌더링될 때마다 새로 함수를 만들지 않고, 이전에 생성한 동일한 함수 인스턴스를 재사용할 수 있게 해줍니다
memoizedCallback은 의존성 배열 [a, b]에 명시된 값이 바뀔 때만 새로 생성됩니다. a와 b가 변경되지 않으면, 이전에 만들어진 함수 인스턴스를 계속 재사용합니다
const memoizedCallback = useCallback(
() => {
doComething(a,b);
},
[a,b],
);
useCallback의 두 번째 인자인 의존성 배열(dependency array)에 포함된 값이 변경될 때만 새로운 콜백 함수가 생성됩니다
const memoizedCallback = useCallback(
() => {
doComething(a,b);
},
[a,b],
);
useEffect(() => {
memoizedCallback()
}, [memoizedCallback])
위 아래 같은 코드입니다.
useEffect(() => {
doComething(a,b);
}, [a,b])
즉, a 또는 b가 바뀔 때만 콜백 함수가 새로 만들어지고, useEffect도 그때만 실행됩니다
Meomoization
: 프로그램에서 동일한 입력값에 대해 반복적으로 연산이 필요한 경우, 그 결과값을 임시 저장(캐싱)해 두었다가 같은 입력이 들어오면 저장된 결과를 바로 반환하는 최적화 기법입니다. 즉, "이미 계산한 값은 다시 계산하지 않고 저장된 값을 재사용"하여 프로그램을 더 빠르게 동작하게 만듭니다
캐싱
: 임시적으로 결과를 저장하는 공간입니다.
Meomoization 사용 전
Recursion
: 함수가 자기 자신을 다시 호출하는 프로그래밍 기법입니다. 즉, 함수 내부에서 자기 자신을 반복적으로 호출하면서 문제를 더 작은 문제로 쪼개어 해결하는 방식입니다.
n이 1 이하일 때는 1을 반환합니다. (종료 조건) 그렇지 않으면, fib(n-1)과 fib(n-2)를 재귀적으로 호출해서 그 결과를 더한 값을 반환합니다.
const fib = n => {
if (n <= 1) return 1
return fib(n-1) + fib(n - 2)
}
Meomoization 사용 후
const fib = (n, memo = {}) => {
if (n in memo) return memo[n]; // 캐시된 값이 있으면 바로 반환
if (n <= 1) return 1; // 종료 조건
// 계산 결과를 캐시에 저장
memo[n] = fib(n - 1, memo) + fib(n - 2, memo);
return memo[n];
};
useMemo()
: 컴포넌트가 리렌더링될 때 "비용이 많이 드는 계산 결과"를 메모이제이션(캐싱)하여, 의존성(dependency)이 바뀌지 않으면 이전에 계산한 값을 재사용하도록 해줍니다.
a와 b가 변경될 때만 computeExpensiveValue(a, b)가 실행되고, 그렇지 않으면 이전에 계산한 값을 그대로 반환합니다.
const memoizedValue = useMemo(() => computEcpensiveValue(a,b), [a,b]);
React.memo
: 함수형 컴포넌트의 결과를 "메모이제이션(memoization)"하여, 동일한 props가 전달되면 컴포넌트를 다시 렌더링하지 않고 이전에 렌더링한 결과를 재사용하는 고차 컴포넌트(Higher Order Component, HOC)입니다
const MyComponent = React.memo(function MyComponent(props) {
};
React.memo로 감싸면, props.value가 바뀌지 않는 한 MyComponent는 리렌더링되지 않습니다.
// 일반 함수형 컴포넌트
function MyComponent(props) {
return <div>{props.value}</div>;
}
// React.memo로 감싸서 메모이제이션 적용
export default React.memo(MyComponent);
복잡한 객체나 배열 props의 경우, 커스텀 비교 함수를 사용해 비교 방식을 세밀하게 제어할 수 있습니다
const MyComponent = React.memo(function MyComponent(props) {
// ...컴포넌트 내용...
}, (prevProps, nextProps) => {
// true를 반환하면 리렌더링하지 않음, false면 리렌더링
return prevProps.value === nextProps.value;
});