-
[FE 최적화 기술] Throttle, Debounce, useDeferredValue, useTransition 쉽게 정리하기 😊웹 개발 2025. 7. 21. 00:03
리액트를 쓰다 보면 ‘입력이 느려요’, ‘렌더링이 버벅여요’ 같은 이야기를 들을 수 있다.
(혹은 무한 랜더링...)
그럴 때 우리가 쓸 수 있는 대표적인 성능 최적화 기법 네 가지가 바로 다음과 같다.
위의 두 가지는 일반적인 방법론이고, 뒤의 두 가지 훅은 리액트 18부터 사용할 수 있는 최적화 훅이다.
- 🧂 Throttling
- 💧 Debouncing
- 🕓 useDeferredValue
- 🌀 useTransition
이 글에서는 각 개념이 언제, 왜, 어떻게 쓰이는지를 쉬운 예제와 함께 알아보자.
[🧂 Throttling] 너무 자주 부르지 않게 하기
특정 시간 동안 이벤트가 여러 번 발생하더라도, 일정 시간 간격으로만 함수를 실행하는 방식이다.
언제 사용할까?
- 스크롤, resize 이벤트 같이 계속 발생하는 이벤트를 제한하고 싶을 때
예제 (바닐라 JS + Lodash)
import throttle from 'lodash.throttle'; window.addEventListener('scroll', throttle(() => { console.log('스크롤 감지!'); }, 1000));예제 (React)
const handleScroll = useCallback(throttle(() => { console.log('스크롤'); }, 1000), []); useEffect(() => { window.addEventListener('scroll', handleScroll); return () => window.removeEventListener('scroll', handleScroll); }, []);[💧 Debouncing] 입력이 끝나면 알려주기
입력이 끝났다고 판단되는 순간에만 실행하는 방식입니다. 입력 도중엔 실행 안 함
언제 사용 할까?
- 검색창 입력, 자동완성처럼 사용자가 입력을 끝낼 때만 API 요청을 보내고 싶을 때
예제 (바닐라 JS + Lodash)
import debounce from 'lodash.debounce'; const search = debounce((value: string) => { console.log('검색어:', value); }, 500); <input onChange={(e) => search(e.target.value)} />예제 (React)
const [query, setQuery] = useState(''); const debouncedQuery = useMemo(() => debounce(setQuery, 500), []); return <input onChange={(e) => debouncedQuery(e.target.value)} />;그리고 이제 리액트 훅으로 제공되는 최적화 기능을 살펴보자.
[🕓 useDeferredValue] 급한 것부터 그리고, 나머진 천천히 진행하기
값 업데이트를 좀 더 나중에 처리해서, 급한 UI는 먼저 렌더링할 수 있게 합니다.
언제 사용할까?
- 입력은 즉시 반영되지만, 연산 비용이 큰 컴포넌트 렌더링은 늦추고 싶을 때
예제
import { useState, useDeferredValue } from 'react'; function SearchResults({ query }: { query: string }) { const deferredQuery = useDeferredValue(query); const results = expensiveSearch(deferredQuery); return <ul>{results.map(r => <li key={r}>{r}</li>)}</ul>; }query가 바뀌면 즉시 UI는 반응하지만, 검색 결과는 조금 천천히 렌더링된다. 성능 문제 있을 때 사용할 수 있다.
[🌀 useTransition] 느린 작업은, 배경에서
React에 “이건 급하지 않아요~”라고 알려주는 훅. 낮은 우선순위의 작업을 나중에 처리하게 한다.
언제 사용할까?
- 입력은 빠르게, 무거운 리스트 렌더링은 느리게 하고 싶을 때
- 버튼 클릭 → 화면 전환 시, 로딩을 자연스럽게 하고 싶을 때
예제
import { useState, useTransition } from 'react'; function Search() { const [query, setQuery] = useState(''); const [list, setList] = useState<string[]>([]); const [isPending, startTransition] = useTransition(); const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { const value = e.target.value; setQuery(value); startTransition(() => { const result = expensiveSearch(value); setList(result); }); }; return ( <> <input value={query} onChange={handleChange} /> {isPending && <p>로딩 중...</p>} <ul>{list.map(item => <li key={item}>{item}</li>)}</ul> </> ); }startTransition 안의 코드는 "급하지 않은 작업"으로 처리된다. 입력은 빠르게 반응하고, 무거운 리스트 렌더링은 부드럽게!
startTransition은 꼭 비동기 작업이 아니더라도 오래 걸리는 상태 업데이트 자체에 적용된다.
startTransition 안에서는 API 호출 같은 비동기 작업이 아닌, 무거운 상태 업데이트도 포함될 수 있다.
예: 리스트 필터링, 가상 DOM diff 등.✨ 언제 뭘 써야 할까?
스크롤/리사이즈 이벤트 너무 자주 호출됨 🧂 Throttling 검색창에서 입력 끝날 때만 처리하고 싶음 💧 Debouncing 무거운 컴포넌트를 늦게 렌더링하고 싶음 🕓 useDeferredValue 빠른 입력 + 느린 리스트 렌더링 분리 🌀 useTransition React에서 UX를 최적화하는 건 속도를 높이는 게 아니라 느리게 보여야 할 걸 잘 늦추는 것에 가깝다.
Throttle, Debounce는 JS 기반의 전통적인 성능 최적화,
useDeferredValue, useTransition은 React의 렌더링 스케줄링을 조절하는 최신 기법이다.필요에 따라서 적절한 기법을 사용하여 최적화할 수 있다.
📚 참고자료
🔵 React 공식 문서
- useTransition: https://react.dev/reference/react/useTransition
- useDeferredValue: https://react.dev/reference/react/useDeferredValue
🟣 MDN Web Docs (브라우저 기반 개념)
- requestAnimationFrame (Throttle 설명에 자주 쓰임): https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
- setTimeout (Debounce 구현의 기반): https://developer.mozilla.org/en-US/docs/Web/API/setTimeout
🟠 라이브러리 문서
- Lodash debounce: https://lodash.com/docs/4.17.15#debounce
- Lodash throttle: https://lodash.com/docs/4.17.15#throttle
🟢 블로그 글
- CSS-Tricks – Debounce vs Throttle: https://css-tricks.com/debouncing-throttling-explained-examples/
'웹 개발' 카테고리의 다른 글
package.json 패키지 버전 자동 업데이트 하기 🍕 (npm-check-updates) (0) 2025.09.06 signal에 대해서 알아보고, state와 비교 (React에서는 signal이 부적합한 이유) ⚾️ (3) 2025.08.07 리스트 가상화(Virtualization) - 성능 최적화를 위한 필수 기술 (많은 데이터 표현 시 최적화) (0) 2025.07.04 Next.js 15 App Router에서 PWA 설정하기 🍍 (0) 2025.04.11 vite 플러그인 만들기 (with 간단한 예제 & vite-plugin-pages 분석) 😊 (0) 2025.03.18