React – 상태 관리와 Hooks – 기본 Hooks – 3 – useMemo와 useCallback 최적화 목적과 차이

React – 상태 관리와 Hooks – 기본 Hooks – 3 – useMemo와 useCallback 최적화 목적과 차이

React – 상태 관리와 Hooks – 기본 Hooks – 3 – useMemo와 useCallback 최적화 목적과 차이

안녕하세요! 😊
이번 시간에는 React에서 성능 최적화에 중요한 역할을 하는 두 가지 훅,
바로 useMemouseCallback에 대해 알아보겠습니다!

이 둘은 이름도 비슷하고 사용법도 비슷해서 헷갈리기 쉬운데요,
“계산 결과를 기억할 건지”, **”함수를 기억할 건지”**에 따라 구분하면 훨씬 이해가 쉬워요.

그럼 한 번, 각각 어떤 상황에서 사용하고 어떤 차이가 있는지, 실제 예시와 함께 쏙쏙 이해해볼까요?


1. 왜 최적화가 필요할까요?

React는 상태나 props가 바뀌면 컴포넌트를 리렌더링해요.
이때 렌더링 안에서 무거운 연산이 계속 반복되거나, 불필요한 함수 재생성이 발생하면 성능이 나빠질 수 있어요.

예를 들면…

매 렌더링마다 for문 100만 번 돌리면…
🐢 화면도 느려지고 사용자도 답답해지겠죠?

바로 이런 상황을 방지하기 위해 사용하는 게 useMemouseCallback입니다!


2. useMemo란?

👉 계산된 값을 기억(Memoization)

useMemo값(value) 을 캐싱해서, 의존성이 바뀌지 않으면 이전 계산 결과를 재사용하게 해줘요.

const memoizedValue = useMemo(() => {
  return 복잡한계산(값);
}, [값]);

예제: 무거운 계산

const expensiveCalc = (num) => {
  console.log('무거운 계산 중...');
  let result = 0;
  for (let i = 0; i < 100000000; i++) {
    result += num;
  }
  return result;
};

function MyComponent({ number }) {
  const result = useMemo(() => expensiveCalc(number), [number]);

  return <div>계산 결과: {result}</div>;
}
  • number가 변경될 때만 expensiveCalc가 실행되고,
  • 동일하면 계산 결과를 기억해서 재사용해요! 성능 굿굿!

3. useCallback이란?

👉 함수를 기억(Memoization)

useCallback함수(function) 를 캐싱해서, 의존성이 같으면 이전 함수를 그대로 재사용해요.

const memoizedFn = useCallback(() => {
  함수내용;
}, [의존성]);

예제: 자식 컴포넌트에 함수 전달

function Parent() {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    console.log('버튼 클릭!');
  }, []); // 한 번만 생성됨

  return (
    <>
      <Child onClick={handleClick} />
      <button onClick={() => setCount(count + 1)}>증가</button>
    </>
  );
}

function Child({ onClick }) {
  console.log('Child 렌더링!');
  return <button onClick={onClick}>자식 버튼</button>;
}

🔍 여기서 만약 useCallback을 쓰지 않으면 handleClick 함수가
Parent가 렌더링될 때마다 새로 생성되고,
그걸 받는 Child도 불필요하게 리렌더링됩니다!


4. useMemo vs useCallback 비교

항목 useMemo useCallback
기억하는 대상 계산된 선언된 함수
사용 목적 계산 결과 재사용 동일 함수 객체 재사용
반환값 값 (예: 숫자, 배열 등) 함수
사용 시기 무거운 연산 결과를 저장하고 싶을 때 props로 전달되는 함수를 재사용하고 싶을 때
자주 쓰는 곳 리스트 필터링, 합계 계산 등 자식 컴포넌트에 함수 props 넘길 때

5. 실전 예제: 둘 다 활용

function TodoApp({ todos }) {
  const [filter, setFilter] = useState('all');

  const filteredTodos = useMemo(() => {
    console.log('필터링 중...');
    return todos.filter(todo => {
      if (filter === 'completed') return todo.done;
      if (filter === 'active') return !todo.done;
      return true;
    });
  }, [todos, filter]);

  const toggleTodo = useCallback((id) => {
    console.log(`할 일 ${id} 토글`);
  }, []);

  return (
    <div>
      <select onChange={e => setFilter(e.target.value)}>
        <option value="all">전체</option>
        <option value="active">활성</option>
        <option value="completed">완료</option>
      </select>
      {filteredTodos.map(todo => (
        <TodoItem key={todo.id} todo={todo} onToggle={toggleTodo} />
      ))}
    </div>
  );
}
  • useMemo: 매 렌더링마다 todos.filter()를 반복하지 않도록 필터링 결과를 저장
  • useCallback: toggleTodo 함수가 변하지 않아 자식 컴포넌트 리렌더링 방지

6. 주의해야 할 점

주의사항 설명
무분별한 사용 ❌ 간단한 연산이나 함수에 useMemo, useCallback을 사용하면 오히려 비용이 증가해요
의존성 정확히 지정 [값]이 누락되면 오래된 값이 캐싱될 수 있음
필요할 때만 사용 실제 렌더링에 성능 문제가 있을 때 도입하는 것이 좋아요

7. 비유로 이해해볼까요?

  • useMemo“계산기 기억 기능”!
    → 똑같은 식이면 또 계산하지 않고, 예전 결과 꺼내줌!

  • useCallback“함수 저장소”!
    → 똑같은 기능의 함수면, 새로 만들지 않고 예전 함수 재사용!


8. 요약 정리

기억 대상 사용 목적 리렌더 영향
useMemo 무거운 계산 최적화 없음
useCallback 함수 props 최적화 없음

👉 둘 다 렌더링을 막는 게 아니라,
불필요한 계산 또는 함수 재생성을 줄여주는 훅이에요!


마무리하며

성능 최적화는 너무 일찍 걱정할 필요는 없지만,
useMemo와 useCallback은 꼭 알아야 할 기본기 중 하나랍니다!

특히 컴포넌트가 많아지거나, 렌더링이 자주 발생하거나,
props로 함수를 자주 넘기는 구조라면 꼭 활용해보세요.

🌱 오늘 배운 걸 요약하면?

🔹 useMemo: 무거운 계산 결과를 기억해서 반복하지 않기
🔹 useCallback: 함수를 기억해서 자식 컴포넌트 리렌더 방지

다음 시간엔 useContext와 상태 공유 방식에 대해 알아보며
더 구조적인 리액트 앱 설계에 한 걸음 더 나아가볼게요! 😊💪

답글 남기기