반응형
useMemo - 계산된 "값"을 메모이제이션
- 계산량이 많은 함수가 리렌더링마다 호출될 때
- 의존성이 바뀌지 않았다면 이전 값을 재사용하고 싶을 때
더보기
src/components/ExpensiveComponent.tsx
아래의 코드에서는 수많은 반복을 통해 값이 계속 바뀌어 리렌더링되어 성능이 떨어진다.
import React, { useMemo, useState } from "react";
export default function ExpensiveComponent() {
const [count, setCount] = useState(0);
const [toggle, setToggle] = useState(false);
const expensiveValue = useMemo(() => {
console.log('Expensive calculation...');
let total = 0;
for (let i = 0; i < 100000000 ; i++) {
total += 1;
}
return total + count;
}, [count]);
return (
<div className="p-4 m-4 bg-gray-100">
<p>Count: {count}</p>
<button className="p-4 m-4 bg-blue-300" onClick={() => setCount((c) => c + 1)}> + 1 </button>
<button className="p-4 m-4 bg-blue-300" onClick={() => setToggle((t) => !t)}> Toggle </button>
<p>Expensive Value: {expensiveValue} </p>
</div>
)
}
src/components/UseMemoExample.tsx
useMemo라는 훅을 통해 미리 계산해서 값을 출력해줌
import React, { useMemo, useState } from "react";
function slowFunction(num: number) {
console.log('무거운 계산 실행 중...');
let result = 0;
for (let i = 0; i < 1e8; i++) {
result += num * Math.random();
}
return result;
}
export default function UseMemoExample() {
const [count, setCount] = useState(0);
const [other, setOther] = useState(false);
const expensiveResult = useMemo(() => {
return slowFunction(count);
}, [count]);
return (
<div style={{ padding: 20 }}>
<h2>useMemo 예제</h2>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}> + 1 증가 </button>
<button onClick={() => setCount(count)}> + 0 증가 </button>
<button onClick={() => setOther(!other)}> Toggle : {other.toString()} </button>
<p> 계산 결과: {expensiveResult.toFixed(2)} </p>
</div>
)
}
src/pages/MemoTest.tsx
import ExpensiveComponent from "../components/ExpensiveComponent";
import UseMemoExample from '../components/UseMemoExample';
export default function MemoTest() {
return (
<div>
<h2>useMemo 테스트</h2>
<ExpensiveComponent />
<UseMemoExample />
</div>
);
}
src/App.tsx
import React from 'react';
import logo from './logo.svg';
import './App.css';
import MemoTest from './pages/MemoTest'
function App() {
return (
<div className="App">
<MemoTest />
</div>
);
}
export default App;

계산이 무거운데, 리렌더링 되면 또 계산해야 돼?에 대한 해결책.
useCallback - 함수를 메모이제이션
- 자식 컴포넌트에 콜백 함수를 props로 넘길 때
- 의존성이 바뀌지 않았다면 기존 함수를 재사용하고 싶을 때
더보기
src/components/Parent.tsx
import React, { useState, useCallback } from "react";
const Button = React.memo(
( { onClick, label }: {onClick: () => void, label: string}) => {
console.log(`Rendering: ${label}`);
return (<button onClick={onClick}> {label} </button>);
});
export default function Parent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount((prev) => prev + 1);
}, []);
return (
<div>
<p>Count: {count}</p>
<Button onClick={handleClick} label="Increment"/>
</div>
)
}
src/pages/MemoTest.tsx
import ExpensiveComponent from "../components/ExpensiveComponent";
import UseMemoExample from '../components/UseMemoExample';
import Parent from '../components/Parent'
export default function MemoTest() {
return (
<div>
{/* <ExpensiveComponent /> */}
{/* <UseMemoExample /> */}
<Parent />
</div>
);
}
src/App.tsx
import React from 'react';
import logo from './logo.svg';
import './App.css';
import MemoTest from './pages/MemoTest'
function App() {
return (
<div className="App">
<MemoTest />
</div>
);
}
export default App;
props가 바뀌었다고 함수가 새로 만들어져서 리렌더링되는 경우에 대한 해결책, React.memo와 함께 사용
+ useCallback 예제 하나 더
더보기
src/components/UseCallback.tsx
import React, { useState, useCallback } from "react";
const Child = React.memo(({ onClick }: { onClick: () => void }) => {
console.log("자식 컴포넌트 렌더링");
return (
<div>
<button onClick={onClick}> 자식 버튼 클릭 </button>
</div>
);
})
export default function UseCallbackExample() {
const [count, setCount] = useState(0);
const [other, setOther] = useState(false);
const handleClick = useCallback(() => {
setCount((prev) => prev + 1);
}, []);
return (
<div style={{ padding: 20 }}>
<h2>UseCallback 예제</h2>
<p>Count: {count}</p>
<button onClick={() => setOther(!other)}> Toggle: {other.toString()} </button>
<Child onClick={handleClick} />
</div>
)
}
src/pages/MemoTest.tsx
import ExpensiveComponent from "../components/ExpensiveComponent";
import UseMemoExample from '../components/UseMemoExample';
import Parent from '../components/Parent'
import UseCallbackExample from "../components/UseCallbackExample";
export default function MemoTest() {
return (
<div>
{/* <ExpensiveComponent /> */}
{/* <UseMemoExample /> */}
<Parent />
<UseCallbackExample />
</div>
);
}
src/App.tsx
import React from 'react';
import logo from './logo.svg';
import './App.css';
import MemoTest from './pages/MemoTest'
function App() {
return (
<div className="App">
<MemoTest />
</div>
);
}
export default App;
클릭할 때마다 리렌더링이 되는 것이 아닌 그대로 오름


※ 주의사항
- 무조건 사용하는 것이 아니라, 성능 문제를 확인하고 도입하는 것이 중요
- useMemo, useCallback 자체도 비용이 있기 때문에 과도한 사용은 오히려 성능 저하
반응형
'Front-End > React' 카테고리의 다른 글
| React #21 (고급 훅 - useId, useTransition, useImperativeHandle) (1) | 2025.07.21 |
|---|---|
| React #20 (고급 훅 - useLayoutEffect) (1) | 2025.07.21 |
| React #18 (훅, 상태관리 - useContext) (2) | 2025.07.17 |
| React #17 (훅, 상태관리 - useRef) (1) | 2025.07.17 |
| React #16 (useState, useEffect 연습문제) (1) | 2025.07.17 |