
부트캠프에서 했던 마지막 프로젝트에서 검색 기능을 맡게 되었다!
일반적으로는 엔터를 누르거나 검색 버튼을 눌러야 결과가 나오는 방식이 많지만,
이번에는 실시간으로 입력할 때마다 결과가 나타나는 방식으로 구현하게 되었다.

처음에는 검색창에 글자를 입력할 때마다 바로 API 요청을 보내는 방식으로 구현했었는데, 이렇게 하니 글자를 하나 칠 때마다 요청이 계속 발생해서 네트워크 탭이 수많은 API 요청으로 도배되는 상황이 벌어졌다 🫢
이걸 어떻게 해결할 수 있을까 고민하다가 debounce 라는 개념을 알게 되었고, 입력이 끝난 뒤 일정 시간 동안 추가 입력이 없을 때만 요청을 보내는 방식으로 수정해 최적화를 할 수 있었다.
🤔 Debounce
디바운스는 과도한 요청으로 발생할 수 있는 과부하, 성능의 저하를 개선할 수 있는 여러 방법 중에 하나인데 연속적으로 발생하는 이벤트를 그룹화하여 마지막 이벤트만 처리하는 방식이다. 이를 검색 기능에 적용하면, 사용자의 타이핑이 일정 시간 동안 멈춘 후에만 검색 요청을 보내도록 할 수 있다.
이러한 반복적으로 실행되는 코드의 성능을 향상시키기 위해 같이 거론되는 기술에는 throttle도 있는데, throttle에 대해서도 알아보자!
✅ Throttle
쓰로틀이란 이벤트를 일정한 주기마다 발생하도록 하는 기술이다. 예를 들어 쓰로틀의 설정시간으로 1ms을 주게 되면 해당 이벤트는 1ms 동안 최대 한번만 발생하게 된다. 즉, 마지막 함수가 호출된 후 일정 시간이 지나기 전에 다시 호출되지 않도록 하는 것이다.
📚 Debounce와 Throttle의 차이점
Debounce와 Throttle의 가장 큰 차이점은 Throttle은 적어도 특정 시간마다 정기적으로 기능 실행을 보장한다는 것이다. Debounce는 아무리 많은 이벤트가 발생해도 모두 무시하고 특정 시간 사이에 어떤 이벤트도 발생하지 않았을 때 딱 한번만 마지막 이벤트를 발생시키는 기법이다. 따라서 5ms 가 지나기전에 계속 이벤트가 발생할 경우 콜백에 반응하는 이벤트는 발생하지 않고 계속 무시된다.
🙋🏻♀️ 언제 사용하는 것이 좋을까?
1️⃣ Debounce
이벤트가 끝난 후 일정 시간 동안 추가 이벤트가 없을 때 동작하게 만드는 방식이다.
실시간 자동 검색 기능처럼 사용자가 글자를 입력할 때마다 검색 결과가 바뀌는 경우에 유용하다.
입력을 다 마칠 때까지 기다렸다가 한번만 요청을 보내므로 불필요한 API 요청을 줄이고 서버 과부하를 줄일 수 있다.
2️⃣ Throttle
일정 시간 간격으로만 함수가 실행되도록 제한하는 방식이다.
스크롤 이벤트나 리사이즈 이벤트처럼 짧은 시간안에 많은 양의 API가 호출되는 상황에서 유용하다.
지정한 시간 간격마다 한 번씩 실행되므로 화면 렌더링 성능 저하를 막는데 도움이 된다.
🍒 useDebounce 훅 구현하기
"use client";
import { useEffect, useState } from "react";
export default function useDebounce<T>(value: T, delay: number): T {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebounced(value);
}, delay);
return () => clearTimeout(handler);
}, [value, delay]);
return debounced;
}
value가 바뀔 때마다 바로 반영하지 않고, 일정 시간이 지난 후에 값을 업데이트한다.
useState로 debouced 값을 관리하고, useEffect 안에서 setTimeout을 이용해 delay만큼 기다리고 다음 값을 설정한다.
만약 그 전에 값이 바뀌게 되면 clearTimeout으로 이전 타이머를 취소하고, 새로운 타이머를 설정해주는 방식이다.
🍭 Debounce를 실제로 컴포넌트에 적용한 코드 일부
export default function BoardList({ keyword }: BoardListProps) {
const debouncedKeyword = useDebounce(keyword, 300);
const { data: articleList = [], totalCount = 0 } = useArticleList({
page,
pageSize,
orderBy,
keyword: debouncedKeyword,
});
return (
<div className="flex flex-col mt-[5rem]">
<IfElse
condition={debouncedKeyword.trim() !== "" && articleList.length === 0}
then={<EmptyCard keyword={debouncedKeyword} />}
else={
<>
<ul className="grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-6 mb-10">
{articleList.map((post: ArticleType) => (
<li key={post.id}>
<LongCard article={post} />
</li>
))}
</ul>
</>
}
/>
</div>
);
}
keyword는 사용자가 검색창에 입력하는 값이고, 이 값을 바로 API 요청에 넘기지 않고, useDebounce 훅을 이용해 300ms 지연된 값을 따로 만든 다음에 실제 검색 API를 요청하는 구조이다. debouncedKeyword가 빈 문자열이 아니면서, 검색 결과가 없을 경우에는 결과 없음 컴포넌트가 보이게, 검색 결과가 있으면 결과 카드를 보여주게 하는 구조로 구현하였다.

Debounce를 적용하고 나니 검색어를 입력하고 나서 딱 한번만 요청이 보내진 것을 확인할 수 있었다 ㅎㅎ
'React' 카테고리의 다른 글
| [React] 성능 최적화를 위한 useMemo, useCallback, React.memo (5) | 2025.08.21 |
|---|---|
| [React] Lazyloading으로 Code Splitting하기 (1) | 2025.07.11 |
| [React] useCallback이란? (0) | 2024.01.22 |
| [React] useMemo란? (2) | 2024.01.18 |
| [React] useReducer란? (0) | 2024.01.17 |