서론
React 최적화 관련 글 보다가 useMemo랑 useCallback이라는 Hook을 발견했음
둘 다 성능 최적화를 위한 거라고 하는데, 처음엔 "둘이 뭐가 다른 거야?" 싶었음
코드 보면 사용법도 비슷하고, 둘 다 의존성 배열 쓰는 것도 같아서 헷갈렸음
근데 찾아보니까 하나는 값을 메모이제이션하고, 하나는 함수를 메모이제이션하는 거였음
그래서 useMemo와 useCallback의 차이점과 언제 쓰는지 정리해봄
본론
useMemo와 useCallback은 둘 다 불필요한 연산이나 리렌더링을 방지하기 위한 Hook임
하지만 메모이제이션하는 대상이 다름
메모이제이션이 뭐냐면?
메모이제이션(Memoization)은 이전에 계산한 값을 저장해뒀다가 재사용하는 기법임
- 같은 계산을 반복하지 않아도 됨
- 성능이 향상됨
- React에서는 불필요한 리렌더링을 줄이는 데 사용함
useMemo: 값을 메모이제이션
useMemo는 계산된 값을 저장해둠
복잡한 연산의 결과를 기억해뒀다가, 의존성 배열의 값이 바뀌지 않으면 이전에 계산한 값을 그대로 사용함
useMemo 사용 예시
import { useState, useMemo } from 'react';
function ExpensiveComponent() {
const [count, setCount] = useState(0);
const [input, setInput] = useState('');
// 복잡한 계산 (시간이 오래 걸림)
const expensiveValue = useMemo(() => {
console.log('복잡한 계산 실행됨');
let result = 0;
for (let i = 0; i < 1000000000; i++) {
result += i;
}
return result;
}, [count]); // count가 바뀔 때만 다시 계산
return (
<div>
<p>계산 결과: {expensiveValue}</p>
<button onClick={() => setCount(count + 1)}>Count 증가</button>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="입력해보세요"
/>
</div>
);
}
useMemo 없이 쓰면?
- input에 글자 하나 입력할 때마다 복잡한 계산이 다시 실행됨
- 화면이 느려지고 버벅거림
useMemo 쓰면?
- count가 바뀔 때만 계산이 실행됨
- input 입력해도 이전에 계산한 값 재사용
- 성능 상승
useCallback: 함수를 메모이제이션
useCallback은 함수 자체를 저장해둠
의존성 배열의 값이 바뀌지 않으면 이전에 만든 함수를 그대로 재사용함
useCallback 사용 예시
import { useState, useCallback } from 'react';
function ParentComponent() {
const [count, setCount] = useState(0);
const [input, setInput] = useState('');
// 함수를 메모이제이션
const handleClick = useCallback(() => {
console.log('버튼 클릭됨');
console.log('현재 count:', count);
}, [count]); // count가 바뀔 때만 함수 재생성
return (
<div>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
/>
<ChildComponent onClick={handleClick} />
</div>
);
}
function ChildComponent({ onClick }) {
console.log('ChildComponent 렌더링됨');
return <button onClick={onClick}>클릭</button>;
}
useCallback 없이 쓰면?
- 부모 컴포넌트가 리렌더링될 때마다 handleClick 함수가 새로 생성됨
- 자식 컴포넌트는 props가 바뀐 걸로 인식해서 불필요하게 리렌더링됨
useCallback 쓰면?
- count가 바뀔 때만 함수가 새로 생성됨
- input 입력해도 함수는 그대로라서 자식 컴포넌트가 리렌더링 안 됨
useMemo vs useCallback 비교
useMemo
- 계산된 값을 메모이제이션
- 반환값: 계산 결과 (숫자, 문자열, 객체, 배열 등)
- 사용 상황: 복잡한 연산 결과를 재사용할 때
const value = useMemo(() => {
return 복잡한계산();
}, [dependency]);
useCallback
- 함수 자체를 메모이제이션
- 반환값: 함수
- 사용 상황: 자식 컴포넌트에 함수를 props로 넘길 때
const handleClick = useCallback(() => {
console.log('클릭됨');
}, [dependency]);
핵심 차이
- useMemo는
() => 값형태로 값을 반환 - useCallback은
() => {}형태로 함수 자체를 반환 - 사실
useCallback(fn, deps)는useMemo(() => fn, deps)와 같음
언제 사용해야 할까?
useMemo 사용 시점
- 복잡한 계산이 있을 때
- 배열이나 객체를 생성할 때 (참조값이 바뀌는 걸 방지)
- 리스트 필터링이나 정렬 같은 연산
const filteredList = useMemo(() => {
return list.filter(item => item.price > 1000);
}, [list]);
useCallback 사용 시점
- 자식 컴포넌트에 함수를 props로 넘길 때
- useEffect의 의존성 배열에 함수를 넣을 때
- 이벤트 핸들러를 최적화할 때
const handleDelete = useCallback((id) => {
setItems(items.filter(item => item.id !== id));
}, [items]);
주의할 점
무조건 쓴다고 좋은 건 아님
- 간단한 연산에는 오히려 성능이 떨어질 수 있음
- 메모이제이션 자체도 비용이 들기 때문
- 성능 문제가 실제로 있을 때만 사용하는 게 좋음
React.memo와 함께 써야 효과적임
- 자식 컴포넌트를 React.memo로 감싸야 useCallback 효과가 제대로 나타남
- 그냥 쓰면 props는 같아도 자식이 리렌더링될 수 있음
const ChildComponent = React.memo(({ onClick }) => {
console.log('ChildComponent 렌더링됨');
return <button onClick={onClick}>클릭</button>;
});
결론
useMemo는 값을 메모이제이션하고, useCallback은 함수를 메모이제이션함
둘 다 성능 최적화를 위한 Hook이지만 사용 목적이 다름
- 복잡한 계산 결과 저장: useMemo
- 함수를 props로 넘길 때: useCallback
처음엔 "언제 써야 하지?" 싶었는데, 실제로 성능 문제가 생길 때 적용하는 게 답인 것 같음
무분별하게 쓰면 오히려 코드만 복잡해지니까 필요한 곳에만 쓰는 게 중요함
앞으로 최적화 필요한 부분 생기면 useMemo와 useCallback 적절히 활용해봐야겠음!
'프론트엔드 > React' 카테고리의 다른 글
| (React) Custom Hooks 만들기 (0) | 2025.12.02 |
|---|---|
| (React) Framer Motion 애니메이션 만들어보기 (0) | 2025.12.01 |
| (React) props drilling 문제와 해결법 (0) | 2025.11.18 |
| (React) useRef는 언제쓰는걸까? (0) | 2025.11.17 |
| (React) 리액트란? (0) | 2025.11.16 |