티스토리 뷰

React로 애플리케이션을 개발하다 보면 "성능 최적화"라는 숙제에 직면하게 됩니다. 이때 가장 먼저 떠올리는 것이 useMemo와 useCallback이죠. 하지만 이 도구들은 무조건 많이 쓴다고 성능이 좋아지는 '마법의 지팡이'가 아닙니다. 오히려 잘못 사용하면 메모리 낭비와 코드 복잡성만 높일 수 있습니다.
오늘은 이 두 Hook의 정확한 사용 시점과 주의사항을 풍부한 예제를 통해 알아보겠습니다.
1. useMemo: 계산된 값의 재사용
useMemo는 특정 연산의 결과값을 메모리에 저장(메모이제이션)해 두었다가, 의존성 배열에 있는 값이 변경될 때만 다시 계산하는 Hook입니다.
✅ 언제 써야 할까?
A. 복잡하고 비용이 큰 계산이 포함될 때
데이터가 수만 개인 배열을 필터링하거나, 복잡한 수학적 계산을 렌더링마다 반복해야 한다면 useMemo가 효과적입니다.
import React, { useMemo } from 'react';
function ExpensiveComponent({ data, filterQuery }) {
// 데이터가 매우 클 경우, 렌더링마다 filter를 실행하는 것은 낭비입니다.
const filteredData = useMemo(() => {
console.log('복잡한 계산 수행 중...');
return data.filter(item => item.name.includes(filterQuery));
}, [data, filterQuery]); // data나 filterQuery가 바뀔 때만 다시 계산
return (
<ul>
{filteredData.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
);
}
B. 참조 동일성(Referential Integrity) 유지가 필요할 때
자식 컴포넌트가 React.memo로 감싸져 있고, 부모가 객체를 prop으로 전달할 때 중요합니다. JavaScript에서 객체는 내용이 같아도 참조 주소가 다르면 "다른 것"으로 인식되기 때문입니다.
❌ 언제 쓰지 말아야 할까?
- 단순한 연산: 변수 두 개를 더하거나, 간단한 문자열을 조합하는 정도는 useMemo를 사용하는 비용(메모리 할당 및 의존성 비교)이 계산 비용보다 더 클 수 있습니다.
- 의존성 배열이 너무 자주 바뀌는 경우: 매 렌더링마다 의존성이 바뀌어 어차피 매번 계산된다면 최적화의 의미가 없습니다.
2. useCallback: 함수의 재사용
useCallback은 값 대신 함수 자체를 메모이제이션합니다. 컴포넌트가 리렌더링될 때마다 함수가 새로 생성되는 것을 방지합니다.
✅ 언제 써야 할까?
A. React.memo와 함께 사용하는 자식 컴포넌트의 props로 함수를 전달할 때
이것이 useCallback의 가장 흔하고 중요한 용도입니다.
import React, { useState, useCallback } from 'react';
// 자식 컴포넌트: React.memo로 불필요한 리렌더링 방지
const SearchButton = React.memo(({ onClick }) => {
console.log('SearchButton 렌더링');
return <button onClick={onClick}>검색하기</button>;
});
function ParentComponent() {
const [text, setText] = useState('');
// useCallback이 없다면 부모가 렌더링될 때마다 handleSearch가 새로 생성됨
// 이로 인해 SearchButton도 (props가 바뀌었다고 판단하여) 리렌더링됨
const handleSearch = useCallback(() => {
console.log('검색 실행:', text);
}, [text]);
return (
<div>
<input value={text} onChange={(e) => setText(e.target.value)} />
<SearchButton onClick={handleSearch} />
</div>
);
}
B. useEffect 내의 의존성으로 함수가 들어갈 때
함수 내부에서 상태값을 참조하고 그 함수를 useEffect에서 호출한다면, 함수의 참조값이 변할 때마다 효과가 실행되는 무한 루프를 방지하기 위해 사용합니다.
❌ 언제 쓰지 말아야 할까?
- 일반적인 이벤트 핸들러: React.memo를 쓰지 않는 일반 HTML 요소(button, input 등)에 전달하는 함수는 굳이 useCallback으로 감쌀 필요가 없습니다. 현대의 브라우저에서 함수 생성 자체는 매우 가벼운 작업입니다.
3. 최적화 도구 사용 전 체크리스트
최적화 코드를 작성하기 전에 스스로에게 다음 세 가지 질문을 던져보세요.
- "성능 저하가 실제로 체감되는가?"
- Chrome DevTools의 Profiler 탭을 사용하여 실제로 렌더링 병목이 발생하는지 확인하세요.
- "컴포넌트 구조를 먼저 개선할 수는 없는가?"
- 상태를 더 하위 컴포넌트로 내리거나(State Colocation), children prop을 활용해 컴포넌트 구성을 바꾸는 것만으로도 해결되는 경우가 많습니다.
- "메모이제이션 비용이 더 큰 것은 아닌가?"
- 모든 Hook 사용은 메모리 소비와 의존성 비교 로직이라는 비용을 수반합니다.
요약
도구목적주요 사용처
| useMemo | 값(Value)의 재사용 | 복잡한 데이터 처리, 객체의 참조 동일성 유지 |
| useCallback | 함수(Function)의 재사용 | React.memo 자식에게 전달하는 콜백 함수 |
| React.memo | 컴포넌트의 재사용 | Props가 변하지 않으면 리렌더링 건너뜀 |
성능 최적화는 '예방'보다 '진단'이 우선입니다. 코드의 가독성을 유지하면서 꼭 필요한 곳에 전략적으로 useMemo와 useCallback을 적용하는 것이 진정한 숙련자의 태도일 것입니다.
'Frontend > React' 카테고리의 다른 글
| useRef 완벽 가이드: DOM 접근부터 데이터 저장까지 (0) | 2026.02.25 |
|---|---|
| 리액트 마스터하기: 커스텀 훅(Custom Hooks)으로 코드 중복 완벽하게 제거하기 (0) | 2026.02.24 |
| useEffect 사용법 완벽 가이드: 입문부터 흔히 하는 실수까지 (0) | 2026.02.24 |
| 리액트 컴포넌트 생명주기(Lifecycle) 한눈에 파악하기 (0) | 2026.02.24 |
| 리액트 초보 탈출: Props vs State 5분 완성 가이드 (0) | 2026.02.24 |
- Total
- Today
- Yesterday
- LLM
- prompt engineering
- on-device ai
- MSA
- SSR
- sLLM
- CSS
- 카카오
- 구글
- 협력
- 웹기초
- TypeScript
- react
- HBM
- Javascript
- 엣지컴퓨팅
- HTML
- Nextjs
- 멀티모달
- AI
- 스마트안경
- CSR
- It용어
- Rag
- java
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 |