메모이제이션은 무거운 함수 호출의 결과를 캐싱하고 유사한 입력이 반복적으로 발생할 때 반환하여 컴퓨터 프로그램을 가속화하는 최적화 기술입니다.
쉽게 말해서 계산된 값을 자료구조에 저장하고 이후 같은 계산을 반복하지 않고 자료구조에서 꺼내 재사용하는 것을 말합니다.
React 함수형 컴포넌트 내의 props가 변경되면 기본적으로 전체 컴포넌트가 다시 렌더링됩니다.
컴포넌트 내부의 값이 변경되면 전체 컴포넌트가 다시 렌더링되고 값이나 props가 변경되지 않은 모든 함수나 컴포넌트도 다시 렌더링이 됩니다.
이러한 전체 컴포넌트의 렌더링은 성능저하의 원인이 되지만 메모이제이션을 사용해서 불필요한 랜더링을 방지할 수 있습니다.
대표적으로 React 함수형 컴포넌트의 useMemo와 useCallback 훅은 퍼포먼스 최적화를 위하여 사용됩니다.
리엑트의 공식문서 상에서 정의하는 두 속성은 다음과 같습니다.
useMemo는 재렌더링 사이에 계산 결과를 캐싱할 수 있게 해주는 React Hook 입니다.
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
➡️ useMemo는 메모이제이션된 값을 반환
(deps 배열의 요소가 변경되지 않는 이상 함수의 반환값을 새로 계산하지 않음)
useCallback은 리렌더링 간에 함수 정의를 캐싱해 주는 React Hook입니다.
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
➡️ useCallback은 메모이제이션된 콜백을 반환
(deps 배열의 요소가 변경되지 않는 이상 함수를 새로 생성하지 않음)
메모이제이션은 성능 최적화에 유용하지만 모든 상황에서 필요한 것은 아니기 때문에 적절한 사용이 필요합니다.
복잡한 계산을 수행하는 경우 메모이제이션을 통해 성능을 크게 향상시킬 수 있습니다.
import React, { useMemo } from 'react';
function FibonacciCalculator({ n }) {
// Fibonacci 수열 계산
const fibonacci = (num) => {
if (num <= 1) return num;
return fibonacci(num - 1) + fibonacci(num - 2);
};
// useMemo를 사용하여 계산 결과를 메모이제이션
const memoizedFibonacci = useMemo(() => fibonacci(n), [n]);
return <div>Fibonacci of {n} is {memoizedFibonacci}</div>;
}
데이터가 자주 변경되지 않는 경우 메모이제이션을 통해 불필요한 계산을 피할 수 있습니다.
import React, { useMemo } from 'react';
function DataProcessor({ data }) {
// 데이터 처리 함수
const processData = (data) => {
return data.map(item => item * 2); // 예: 모든 값을 두 배로
};
// useMemo를 사용하여 처리된 데이터를 메모이제이션
const memoizedProcessedData = useMemo(() => processData(data), [data]);
return (
<ul>
{memoizedProcessedData.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
);
}
자식 컴포넌트에 콜백 함수를 전달할 때 useCallback을 사용하면 함수 참조를 메모이제이션할 수 있습니다.
import React, { useCallback, useState } from 'react';
function ParentComponent() {
const [count, setCount] = useState(0);
// useCallback을 사용하여 handleClick 메모이제이션
const handleClick = useCallback(() => {
setCount(prevCount => prevCount + 1);
}, []); // 의존성이 없으므로 항상 동일한 함수 참조
return (
<div>
<button onClick={handleClick}>Increment</button>
<ChildComponent onClick={handleClick} />
<p>Count: {count}</p>
</div>
);
}
function ChildComponent({ onClick }) {
return <button onClick={onClick}>Child Button</button>;
}
단순한 계산은 메모이제이션의 오버헤드가 계산 비용보다 클 수 있습니다.
mport React from 'react';
function SimpleCalculator({ value }) {
// 단순한 계산
const doubledValue = value * 2; // 메모이제이션이 필요 없음
return <div>Doubled Value: {doubledValue}</div>;
}
import React, { useState } from 'react';
function ClickCounter() {
const [count, setCount] = useState(0);
// 간단한 이벤트 핸들러
const handleClick = () => {
setCount(prevCount => prevCount + 1);
};
return (
<div>
<button onClick={handleClick}>Click Me</button>
<p>Count: {count}</p>
</div>
);
}
메모이제이션은 성능 최적화에 유용하지만, 모든 상황에서 필요한 것은 아닙니다.
복잡한 계산이나 자주 변경되지 않는 데이터 처리에는 메모이제이션을 사용하는 것이 좋지만 단순한 계산이나 자주 변경되는 데이터에는 오히려 성능을 저하시킬 수 있습니다.
따라서 상황에 맞게 적절히 사용해야 할 필요가 있습니다.