들어가며
React를 사용하다 보면 컴포넌트의 리렌더링과 관련된 성능 최적화를 고민해야 할 순간이 찾아옵니다. 이때 유용하게 사용할 수 있는 두 가지 훅이 바로 `useMemo`와 `useCallback`입니다. 이번 글에서는 이 두 훅의 개념, 사용법 그리고 활용 사례까지 알아보겠습니다.
요약
- `useMemo`와 `useCallback`은 React에서 성능 최적화를 위한 강력한 도구입니다.
- 하지만 사용 목적에 맞게 신중히 활용해야 하며, 남용은 피해야 합니다.
- 실무에서 큰 데이터 처리, 자식 컴포넌트 최적화 등 상황에서 유용하게 사용될 수 있습니다.
useMemo, useCallback
useMemo와 useCallback이란?
useMemo
`useMemo`는 값을 메모이제이션하는 데 사용됩니다. 특정 연산의 결과를 캐싱하여 불필요한 재계산을 방지함으로써 성능을 최적화합니다.
- 주요 목적 : 비용이 큰 계산을 효율적으로 처리
- 사용 예 : 복잡한 데이터 필터링 정렬 등
const expensiveCalculation = useMemo(() => {
return computeExpensiveValue(a, b);
}, [a, b]);
useCallback
`useCallback`은 함수를 메모이제이션하는 데 사용됩니다. 동일한 함수 인스턴스를 유지하여 컴포넌트가 불필요하게 리렌더링되지 않도록 합니다.
- 주요 목적 : 함수 참조가 변경되지 않도록 유지
- 사용 예 : 자식 컴포넌트에 콜백 함수를 props로 전달할 때
const handleClick = useCallback(() => {
console.log('Button clicked');
}, []);
return <Button onClick={handleClick} />;
useMemo와 useCallback의 차이
특징 | `useMemo` | `useCallback` |
메모이제이션 | 값(value) | 함수(Function) |
사용 목적 | 복잡한 계산 결과 캐싱 | 동일한 함수 참조 유지 |
리턴값 | 계산된 값 | 메모이제이션된 함수 |
언제 사용해야 할까?
useMemo를 사용하는 경우
1. 비용이 큰 계산을 반복적으로 수행하는 경우
const filteredData = useMemo(() => {
return data.filter(item => item.isActive);
}, [data]);
2. 렌더링 성능을 개선하려고 할 때
예를 들어, 대규모 데이터 셋 필터링, 복잡한 수학 연산 등에 사용합니다.
useCallback을 사용하는 경우
1. 자식 컴포넌트에 콜백을 전달하는 경우
const handleSave = useCallback(() => {
saveData(formData);
}, [formData]);
return <ChildComponent onSave={handleSave} />;
2. 자식 컴포넌트가 `React.memo`로 최적화되어 있는 경우
실무에서의 활용 사례
예시 1: API 데이터를 가공할 때 useMemo 사용
function ProductList() {
const [products, setProducts] = useState([]);
const [searchTerm, setSearchTerm] = useState('');
useEffect(() => {
async function fetchProducts() {
const response = await fetch('/api/products');
const data = await response.json();
setProducts(data);
}
fetchProducts();
}, []);
const filteredProducts = useMemo(() => {
return products.filter(product =>
product.name.toLowerCase().includes(searchTerm.toLowerCase())
);
}, [products, searchTerm]);
return (
<div>
<input
type="text"
placeholder="Search products"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<ul>
{filteredProducts.map(product => (
<li key={product.id}>{product.name}</li>
))}
</ul>
</div>
);
}
예시 2: 이벤트 핸들러를 최적화할 때 useCallback 사용
function ParentComponent() {
const [count, setCount] = useState(0);
const increment = useCallback(() => {
setCount(prev => prev + 1);
}, []);
return <ChildComponent onIncrement={increment} />;
}
const ChildComponent = React.memo(({ onIncrement }) => {
console.log('Child rendered');
return <button onClick={onIncrement}>Increment</button>;
});
주의점
- 불필요한 사용을 피해야 합니다.
- `useMemo`와 `useCallback`을 남용하면 코드 복잡도가 증가합니다.
- 성능 최적화가 필요한 지점을 신중히 판단해야 합니다.
- 종속성 배열의 관리
- 종속성 배열이 정확하지 않으면 예상치 못한 동작이 발생할 수 있습니다.
- ESLint의 `react-hooks/exhaustive-deps` 규칙을 활용하여 관리
- React의 기본 렌더링 성능을 신회
- React는 기본적으로 최적화되어 있으므로, 꼭 필요한 경우에만 사용해야 합니다.
'JAVASCRIPT' 카테고리의 다른 글
[React] 리액트 생명주기 (0) | 2025.01.26 |
---|---|
[React] 배열 렌더링과 key (0) | 2025.01.24 |
[React] 리액트 렌더링 알아보기 (0) | 2025.01.15 |
[React] React 컴포넌트란? 기본 개념편 (0) | 2025.01.10 |
[React] Virtual DOM: 리액트의 핵심 기술 (0) | 2025.01.09 |
※ 쿠팡 파트너스 활동을 통해 일정액의 수수료를 제공받을 수 있습니다.