debounce useMemo vs useCallback

Jinmin Kim·2025년 8월 18일

✅ 1️⃣ 잘못된 경우 (useCallback으로 래핑)

import { useCallback, useState } from "react";
import debounce from "lodash-es/debounce";

export default function SearchBoxBad() {
  const [value, setValue] = useState("");

  // ❌ 이렇게 쓰면 렌더마다 debounce(...)가 실행 → 항상 새 함수 생성
  const handleSearch = useCallback(
    debounce((v: string) => {
      console.log("검색 실행:", v);
    }, 1000),
    []
  );

  return (
    <inputvalue={value}
      onChange={(e) => {
        setValue(e.target.value);
        handleSearch(e.target.value);
      }}
    />
  );
}

🔎 결과

  • 타이핑할 때마다 debounce(...)가 새로 만들어짐
  • 내부 타이머/큐 상태가 초기화됨 → 실제로는 디바운스가 안 먹고 매 입력마다 바로 실행

✅ 2️⃣ 올바른 경우 (useMemo로 debounce 함수 인스턴스 고정)

import { useMemo, useState } from "react";
import debounce from "lodash-es/debounce";

export default function SearchBoxGood() {
  const [value, setValue] = useState("");

  // ✅ debounce 결과 "함수 인스턴스"를 값처럼 메모이즈
  const handleSearch = useMemo(
    () =>
      debounce((v: string) => {
        console.log("검색 실행:", v);
      }, 1000),
    []
  );

  return (
    <inputvalue={value}
      onChange={(e) => {
        setValue(e.target.value);
        handleSearch(e.target.value);
      }}
    />
  );
}

🔎 결과

  • 매 렌더마다 debounce(...)를 새로 안 만들고, 같은 디바운스 함수를 재사용
  • 타이머가 유지되므로 연속 입력은 디바운스 후 마지막 값만 실행
  • 우리가 원하는 “타이핑 멈춘 뒤 1초 후 실행” 동작 정상 동작 ✅

✅ 3️⃣ 쉽게 설명 (한 줄 요약)

  • useCallback“이 함수를 다시 만들지 마” 용 → 함수 본문을 직접 정의할 때 유용
  • useMemo“이 값(=debounce가 만든 함수 객체)을 그대로 재사용해” 용 → 라이브러리에서 만들어주는 함수/객체를 캐싱할 때 유용

즉,

useCallback(fn, deps) = useMemo(() => fn, deps)

하지만 debounce(fn, wait)fn을 감싼 “새 함수 객체”를 리턴하므로 → useMemo로 그 값을 캐싱하는 게 맞습니다.


“디바운스처럼 내부 상태(타이머) 를 가진 함수는 useMemo로 고정”

profile
Let's do it developer

0개의 댓글