메모이제이션이란?
메모이제이션은 프로그래밍에서 이전에 계산한 값을 메모리에 저장해두고, 다음번에 같은 계산을 다시 하려고 할 때 이전에 계산한 값을 그대로 사용하는 기법을 말합니다. (동적 계획법의 한 종류)
마치 메모를 적어두고, 다음에 같은 계산을 할 때 메모를 보고 계산을 생략하는 것과 같습니다.
메모이제이션을 사용하는 이유
메모이제이션을 사용하는 이유는 동일한 계산을 반복할 때, 이전에 계산한 값을 재사용하기 때문에, 연산 속도를 높일 수 있습니다. N번째 피보나치 수를 구하는 함수를 예로 들어보겠습니다.
const fibonacci = (n) => {
if (n < 2) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
};
이 함수는 n이 1이 될 때까지 fibonacci 함수를 재귀적으로 호출합니다. 이렇게 하면, n이 커질수록 함수가 반복적으로 호출되므로, 연산 속도가 느려집니다. 시간 복잡도는 O(2^n)이 됩니다.
이를 개선하기 위해, 메모이제이션을 사용할 수 있습니다.
const fibonacci = (n) => {
const memo = [0, 1];
const fibonacci = (n) => {
if (memo[n] != null) return memo[n];
return (memo[n] = fibonacci(n - 1) + fibonacci(n - 2));
};
return fibonacci(n);
};
이렇게 하면, 이미 계산한 값은 memo 배열에 저장되므로, 다음번에 같은 계산을 할 때는 memo 배열에서 값을 가져와서 연산을 생략할 수 있습니다. 시간 복잡도는 O(n)이 됩니다.
리액트에서 메모이제이션을 사용하는 방법 (useMemo, useCallback)
리액트에서 메모이제이션을 사용하는 방법은 useMemo와 useCallback이 있습니다.
useMemo
useMemo 는 메모이제이션의 값을 반환합니다. useMemo 는 deps 에 변화가 생기면 내부에 정의된 함수를 실행하고, 그 결과를 반환합니다. deps 에 변화가 없다면, 이전에 반환했던 값을 재사용합니다.
import React, { useState, useMemo } from "react";
const MemoizedComponent = () => {
const [count, setCount] = useState(0);
const [text, setText] = useState("");
const fibonacci = useMemo(() => {
return (n) => {
const memo = [0, 1];
const fibonacci = (n) => {
if (memo[n] != null) return memo[n];
return (memo[n] = fibonacci(n - 1) + fibonacci(n - 2));
};
return fibonacci(n);
};
}, []);
return (
<div>
<p>{fibonacci(count)}</p>
<input value={text} onChange={(e) => setText(e.target.value)} />
<button onClick={() => setCount(count + 1)}>증가</button>
</div>
);
};
useCallback
useCallback은 useMemo와 비슷하지만, 함수를 반환합니다. useMemo 는 함수를 실행하고, 그 결과를 반환하지만, useCallback 은 함수 자체를 반환합니다.
const MemoizedComponent = () => {
const [count, setCount] = useState(0);
const [text, setText] = useState("");
const fibonacci = useCallback((n) => {
const memo = [0, 1];
const fibonacci = (n) => {
if (memo[n] != null) return memo[n];
return (memo[n] = fibonacci(n - 1) + fibonacci(n - 2));
};
return fibonacci(n);
}, []);
return (
<div>
<p>{fibonacci(count)}</p>
<input value={text} onChange={(e) => setText(e.target.value)} />
<button onClick={() => setCount(count + 1)}>증가</button>
</div>
);
};
이와 같이 useMemo와 useCallback을 사용하면, 함수형 컴포넌트에서 메모이제이션을 사용할 수 있습니다.
React.memo
React.memo는 컴포넌트의 props가 바뀌지 않았다면, 리렌더링을 방지하는 HOC입니다. PureComponent와 비슷한 역할을 합니다.
const MemoizedComponent = () => {
const [count, setCount] = useState(0);
const [text, setText] = useState("");
const fibonacci = (n) => {
const memo = [0, 1];
const fibonacci = (n) => {
if (memo[n] != null) return memo[n];
return (memo[n] = fibonacci(n - 1) + fibonacci(n - 2));
};
return fibonacci(n);
};
return (
<div>
<p>{fibonacci(count)}</p>
<input value={text} onChange={(e) => setText(e.target.value)} />
<button onClick={() => setCount(count + 1)}>증가</button>
</div>
);
};
const MemoizedComponent = React.memo(MemoizedComponent);
React.memo, useMemo, useCallback의 차이점
React.memo 는 HOC이고, useMemo, useCallback 은 Hook입니다.
useMemo, useCallback 은 함수형 컴포넌트에서 메모이제이션을 사용할 수 있게 해주는 Hook이고, React.memo 는 컴포넌트의 props가 바뀌지 않았다면, 리렌더링을 방지하는 HOC입니다.