useMemo는 useCallback과 비교되는데, 둘 다 메모이제이션을 한다는 점에서 공통적이지만 어떤 것을 반환하느냐의 차이로 정리할 수 있을 것 같다.
메모이제이션 기법은 연산 결과를 어딘가에 저장해두고, 동일한 입력이 들어왔을 때 재연산 하지 않고 저장된 값을 재활용하는 기법이다.
useCallback은 dependencies 안에 있는 값이 바뀌었을 때, 함수를 리턴하는 기능을 갖는다.
useMemo는 마찬가지로 dependencies 안에 있는 값이 바뀌었을 때, 연산의 결과값을 리턴하는 기능을 가진다.
공식문서에서는
React Hook that lets you cache the result of a calculation between re-renders.
리렌더링간 발생하는 연산 결과를 캐시하는 리액트 훅
이라고 소개하고 있다.
기본적인 구조는
const cachedValue = useMemo(calculateValue, dependencies)
이다.
파라미터 calculateValue는 캐시하고자 하는 값을 연산하는 함수가 들어간다(콜백함수).
인자를 취할 수 없으며 어떤 타입의 값이라도 리턴해야 한다. 최초 렌더링 시 호출되며 마지막 렌더링으로부터 변화하는 값이 존재하는 경우 다시 호출한다. 다시 말해서 연산의 결과를 저장하고 나중에 재사용한다.
dependencies는 배열 형태이며 calculatedValue 내부에서 참조되는 값이다. 이 값은 props나 state 그리고 컴포넌트 바디 내부에 선언된 모든 변수나 함수가 해당된다.
가령
"use client";
import { useState } from "react";
export default function useMemoPage() {
const [number, setNumber] = useState(1);
const [dark, setDark] = useState(false);
// 복잡한 계산을 하는 함수를 useMemo로 메모이제이션
const doubleNumber = () => {
// 의도적으로 무거운 연산을 시뮬레이션
for (let i = 0; i < 1000000000; i++) {}
return number * 2;
}
// 테마 스타일을 useMemo로 메모이제이션
const themeStyles = () => ({
backgroundColor: dark ? "#333" : "#fff",
color: dark ? "#fff" : "#333",
padding: "2rem",
margin: "2rem",
borderRadius: "8px",
transition: "all 0.3s ease",
})
return (
<div style={{ padding: "20px" }}>
<h1>useMemo 예시</h1>
<div>
<input
type="number"
value={number}
onChange={e => setNumber(parseInt(e.target.value) || 0)}
style={{ marginRight: "10px" }}
/>
<button
onClick={() => setDark(prevDark => !prevDark)}
style={{ padding: "5px 10px" }}
>
테마 변경
</button>
</div>
<div style={themeStyles()}>
<p>원본 숫자: {number}</p>
<p>계산된 숫자 (x2): {doubleNumber()}</p>
<p>현재 테마: {dark ? "다크" : "라이트"}</p>
</div>
<div style={{ marginTop: "20px" }}>
<h3>설명:</h3>
<ul>
<li>숫자를 변경하면 무거운 계산이 실행되지만 useMemo로 최적화되어 있습니다.</li>
<li>테마 변경 시 스타일 객체가 메모이제이션되어 불필요한 리렌더링을 방지합니다.</li>
</ul>
</div>
</div>
);
}
이런 코드가 있다고 가정할 때
input의 값을 바꾸거나 버튼을 클릭하면 성능면에서 두가지 문제점이 발생한다.
1. doubleNumber 함수는 컴포넌트가 리렌더링 될 때마다 실행된다.
2. themeStyles는 매 렌더링마다 새로운 객체를 생성하게 되어 불필요한 렌더링을 유발할 수 있다.
실제로 doubleNumber에서 1억번의 연산이 발생하게 되어 실제로 실행해보면 다소 느린 것을 확인할 수 있었다.
useMemo를 사용하게 되면 메모이제이션된 값을 반환해줌으로써 불필요한 연산을 거치지 않아도 된다.
export default function useMemoPage() {
const [number, setNumber] = useState(1);
const [dark, setDark] = useState(false);
// 복잡한 계산을 하는 함수를 useMemo로 메모이제이션
const doubleNumber = useMemo(() => {
// 의도적으로 무거운 연산을 시뮬레이션
for (let i = 0; i < 1000000000; i++) {}
return number * 2;
}, [number]); // number가 변경될 때만 재계산
// 테마 스타일을 useMemo로 메모이제이션
const themeStyles = useMemo(() => ({
backgroundColor: dark ? "#333" : "#fff",
color: dark ? "#fff" : "#333",
padding: "2rem",
margin: "2rem",
borderRadius: "8px",
transition: "all 0.3s ease",
}),[dark]); // dark가 변경될 때만 재계산
//...
}
'기술스택 > React.js' 카테고리의 다른 글
[React 19] useReducer (0) | 2025.04.27 |
---|---|
[React 19] useEffect (1) | 2025.03.26 |
[React 19] useContext (0) | 2025.03.25 |
[React 19] useCallback (0) | 2025.03.23 |
[React 19] useActionState (1) | 2025.03.22 |