๐ป Frontend
Debounce์ Throttle
category
๐ป Frontend
Debounce vs ThrottleDebounceDebounce ๊ตฌํDebounce โ ThrottleThrottleThrottle ๊ตฌํThrottle โ Debounce๋ง๋ฌด๋ฆฌReferences
Debounce vs Throttle
์๋น์ค์์ ์ฆ์ ๋ฆฌ๋ ๋๋ง์ด ๋ฐ์ํ๋ค๋ฉด ์ฑ๋ฅ ์ ํ์ ์์ธ์ด ๋ ์๋ ์์ต๋๋ค. ์ฆ์ ๋ฆฌ๋ ๋๋ง์ ๋ฌธ์ ์ ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- ์ฑ๋ฅ ์ ํ: ์ปดํฌ๋ํธ๊ฐ ๋ถํ์ํ๊ฒ ์ฌ๋ ๋๋ง๋๋ฉด ์ฑ๋ฅ์ด ์ ํ๋ ์ ์์ต๋๋ค. ํนํ ์ปดํฌ๋ํธ ๊ณ์ธต ๊ตฌ์กฐ๊ฐ ๊น๊ฑฐ๋ ๋ณต์กํ ๊ฒฝ์ฐ์ ์ฆ์ ๋ฆฌ๋ ๋๋ง์ ์ฑ๋ฅ์ ๋ฏธ์น๋ ์ํฅ์ด ๋ ์ปค์ง๋๋ค.
- ๋ถํ์ํ ๋ฐ์ดํฐ ์์ฒญ: ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋ง๋๋ฉด ํด๋น ์ปดํฌ๋ํธ ๋ด์ ๋ฐ์ดํฐ ์์ฒญ๋ ๋ฐ์ํ ์ ์์ต๋๋ค. ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง์ ๋ฐ๋ผ์ ๋ถํ์ํ ๋ฐ์ดํฐ ์์ฒญ์ ์ ๋ฐํ ์ ์์ต๋๋ค.
- ์ปดํฌ๋ํธ state ์์ค: ์ฆ์ ๋ฆฌ๋ ๋๋ง์ผ๋ก ์ธํด ์ปดํฌ๋ํธ์ ์ํ(state)๊ฐ ์์์น ๋ชปํ๊ฒ ์์ด๋ฒ๋ฆด ์ ์์ต๋๋ค.
๊ทธ๋ฌ๋ ์ฆ์ ๋ฆฌ๋ ๋๋ง์ ํ๋ฉด ์๋ฉ๋๋ค.
์ด๋ฒคํธ ํธ์ถ๋ ์์ธ๋ ์๋๋๋ค. ์ด๋ฒคํธ ๋ฐ์ํ ๋ ๊ด๋ จ ์ฝ๋ฐฑ ์ด๋ฒคํธ ํจ์๋ฅผ ์์ฃผ ํธ์ถํ๊ฒ ๋๋ค๋ฉด ์ฑ๋ฅ ์ ํ์ ์์ธ์ด ๋ ๊ฒ์
๋๋ค. ์๋ฅผ ๋ค์ด ํด๋ฆญ์ด๋ฒคํธ์ฒ๋ผ ๋จ์ํ์ง ์๊ณ ํค๋ณด๋์ ์
๋ ฅ์ด ์ง์์ ์ผ๋ก ๋ฐ์ํ๊ฑฐ๋ ์คํฌ๋กค ์ด๋ฒคํธ๊ฐ ์๊ธฐ๋ฉด (ํนํ, ์คํฌ๋กค ์ด๋ฒคํธ) ํ๋ฒ์ ๋ง์ฐ์ค ๋กค๋ง์ผ๋ก๋ ๋ช์ญ-๋ช๋ฐฑ๋ฒ์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํด๋ฒ๋ฆฝ๋๋ค.
๋ง์ฝ, ์ด๋ฐ ์ด๋ฒคํธ๊ฐ ์ผ์ด๋ ๋๋ง๋ค api์์ฒญ์ ๋ณด๋ด๊ฒ ๋๋ค๋ฉด ์๋ฒ ๊ณผ๋ถํ๊ฐ ์ผ์ด๋ฉ๋๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์ ์๋ฏธํ ์์ ์ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํด ์๋ ๋ ๊ฐ๋
์ ์ ์ฉํฉ๋๋ค.
ย
Debounce
์ฐ์ด์ ์ด๋ฒคํธ ์ค ๋ง์ง๋ง ์ด๋ฒคํธ๋ง ์ธ์ํ๋ค.
๋๋ฐ์ด์ค๋ฅผ ์ ์ฉํ๊ธฐ ์ข์ ์์ ๋ ๋ฐ๋ก ๊ฒ์ ์
๋ ฅ ์ด๋ฒคํธ์
๋๋ค.
์๋ฅผ๋ค์ด ๊ฒ์์ฐฝ์ โ์ ์์จโ์ ์
๋ ฅํ ๊ฒฝ์ฐ ๋ชจ๋ ์
๋ ฅ์ ์์ฒญ์ ๋ณด๋ด๋ ๊ฒ์ด ์๋ ์
๋ ฅ์ด ๋ง๋ฌด๋ฆฌ ๋์์ ๋ ์์ฒญ์ ๋ณด๋ด๋ ๊ฒ์
๋๋ค.
๋ง์ฝ ๋๋ฐ์ด์ค๊ฐ ์ ์ฉ์ด ์๋์ด์๋ค๋ฉด ๋ฌด์๋ฏธ์ ๋จ์ด์๋ ๋ถํ์ํ ์์ฒญ์ ๋ณด๋ผ ๊ฒ์
๋๋ค.
ex. ใ
, ์ ใ
, ์ ์์ค, ์ ์์จ
ย
Debounce ๊ตฌํ
function SearchInput() { const [query, setQuery] = useState(''); const [tmpQuery, setTmpQuery] = useState(query); const handleChange = (e: ChangeEvent<HTMLInputElement>) => setTmpQuery(e.target.value); useEffect(() => { const debounce = setTimeout(() => { return setQuery(tmpQuery); }, 300); //->setTimeout ์ค์ return () => clearTimeout(debounce); //->clearTimeout ๋ฐ๋ก ํ์ด๋จธ ์ ๊ฑฐ }, [tmpQuery]); //->๊ฒฐ๊ตญ ๋ง์ง๋ง ์ด๋ฒคํธ์๋ง setTimeout์ด ์คํ๋จ return ( <> <SearchInputBlock> <div className='search-Input'> <SearchIcon /> <input value={tmpQuery} onChange={handleChange} /> </div> </SearchInputBlock> <SearchResult query={query} /> </> ); }
์ฃผ์ ์ฝ๋๋ ์๋์ ๊ฐ์ต๋๋ค.
useEffect(() => { const debounce = setTimeout(() => { return setQuery(tmpQuery); }, 300); //->setTimeout ์ค์ return () => clearTimeout(debounce); //->clearTimeout ๋ฐ๋ก ํ์ด๋จธ ์ ๊ฑฐ }, [tmpQuery]); //->๊ฒฐ๊ตญ ๋ง์ง๋ง ์ด๋ฒคํธ์๋ง setTimeout์ด ์คํ๋จ
- useEffect์์ tmpQuery์ ๋ณ๊ฒฝ์ ๊ฐ์งํ๋ค. tmpQuery๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ๋ด๋ถ ์ฝ๋ ๋ธ๋ก์ด ์คํ๋๋ค.
- setTimeout ํจ์๋ฅผ ํธ์ถํ์ฌ 300ms ํ์ ์คํ๋๋ ํ์ด๋จธ๋ฅผ ์ค์ ํ๋ค.
์ด ํ์ด๋จธ๋
setQuery(tmpQuery);
๋ฅผ ํธ์ถํ๋ค. - ์ด๋, tmpQuery๋ useEffect์ ์์กด์ฑ ๋ฐฐ์ด์ ํฌํจ๋์ด ์์ผ๋ฏ๋ก, tmpQuery๊ฐ ๋ณ๊ฒจ๋ ๋๋ง๋ค ์๋ก์ด ํ์ด๋จธ๊ฐ ์ค์ ๋๋ค.
- ๋ง์ง๋ง ๋ณ๊ฒฝ๋ tmpQuery์ ๋ํ setQuery๊ฐ ํธ์ถ๋๋ค.
- useEffect์ ๋ฐํ๋ฌธ์์๋ ์ด์ ํ์ด๋จธ๊ฐ ์กด์ฌํ๋ฉด(clearTimeout) ๊ทธ ํ์ด๋จธ๋ฅผ ์ทจ์ํ๋ค. ์ด๋ก์จ ์ด์ ์ ์ค์ ๋ ํ์ด๋จธ๋ ์คํ๋์ง ์๊ฒ ๋ฉ๋๋ค.
โ ๋ฐ๋ผ์, ์ฐ์์ ์ผ๋ก tmpQuery๊ฐ ๋ณ๊ฒฝ๋๋๋ผ๋, ๋ง์ง๋ง ๋ณ๊ฒฝ ์ดํ 300ms๊ฐ ์ง๋ ํ์
setQuery(tmpQuery)
๊ฐ ์คํ๋๋ฏ๋ก, ๋ง์ง๋ง ์ด๋ฒคํธ์ ๋ํ ์ฒ๋ฆฌ๋ง ๋ณด์ฅ๋ฉ๋๋ค.ย
ย
ย
Debounce โ Throttle
๋๋ฐ์ด์ค๋ฅผ ์ ์ฉํ ์์ ๋ฅผ ์ฐ๋กํ๋ง์ผ๋ก ๋ฐ๊พธ์ด ๋ณด๊ฒ ์ต๋๋ค. ์ฃผ์์ฝ๋๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ๋ฐ๊พธ์์ต๋๋ค.
const [throttle, setThrottle] = useState<boolean>(false); useEffect(() => { if (throttle) return; if (!throttle) { setThrottle(true); setTimeout(async () => { setSearchValue(tmpValue); setThrottle(false); }, 300); } }, [tmpValue]);
์
๋ ฅ ์ด๋ฒคํธ๋ฅผ ์ฐ๋กํ๋ง์ผ๋ก ๊ตฌํํ๋ค๋ฉด ํฐ ๋ฌธ์ ๊ฐ ์์ต๋๋ค. 0.3์ด ๊ฐ๊ฒฉ์ผ๋ก ์ด๋ฒคํธ๋ฅผ ๊ฐ์งํ๋๋ฐ
์
๋ ฅ ๊ฐ์ โ์ดใ
โ์ผ๋ก ์ธ์งํ ์ ์๋ค๋ ๊ฒ์
๋๋ค. ์
๋ ฅ์ โ์ด์น์ โ๋ผ๊ณ ํ์ด๋ ์ฐฐ๋์ ์๊ฐ์ผ๋ก tmpValue๋ฅผ ์ด์ํ ๊ฐ์ผ๋ก ์ธ์งํ์ฌ ์ฌ์ฉ์๋ ์ํ์ง ์๋ ํ๋ฉด์ ๋ณผ ์ ์์ต๋๋ค.
ย
Throttle
์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๊ณ ์ ์ผ์ ์ฃผ๊ธฐ๋ง๋ค ์ด๋ฒคํธ๊ฐ ๋ฐ์๋๋๋ก ํ๋ค. (์ผ์ ์ฃผ๊ธฐ๊ฐ ๋๋์ง ์์ผ๋ฉด ์ด๋ฒคํธ๋ฅผ ํธ์ถํ์ง ์๋๋ค.)
์ฐ๋กํ๋ง์ ์ ์ฉํ๊ธฐ ์ข์ ์์ ๋ ๋ฐ๋ก ์คํฌ๋กค์ด๋ ํ์ด์ง resize ์ด๋ฒคํธ์
๋๋ค.
๋ง์ฝ ์คํฌ๋กค ์ด๋ฒคํธ์ ํฐ ์ฐ์ฐ ์ด๋ฒคํธ ํจ์๊ฐ ์๋๋ฐ ํ์ด์ง๋ฅผ ์์์ ์๋๋ก ํ๊บผ๋ฒ์ ์คํฌ๋กค ํ๋ค๊ณ ์๊ฐํด๋ด
์๋ค. ๊ทธ๋ ๋ค๋ฉด ๋ชจ๋ ์ด๋ง๋ค ์คํฌ๋กค ์ด๋ฒคํธ๊ฐ ํธ์ถ๋์ด์ ์๋น์ค ์ฑ๋ฅ ์ ํ์ ์์ธ์ด ๋ ๊ฒ์
๋๋ค. ๊ทธ๋์ ์คํฌ๋กค์ 1์ด๋์ํ ๋ ๊ณ์ ์ด๋ฒคํธ ํจ์๋ฅผ ํธ์ถํ๋ ๊ฒ์ด ์๋ ํน์ ์๊ฐ๋ง๋ค ์ด๋ฒคํธ๊ฐ ๋ฐ์๋๋ ์ฐ๋กํ๋ง์ ์ ์ฉํ ์ ์์ต๋๋ค.
ย
Throttle ๊ตฌํ
export default function useGetWindowScrollHeight() { const [windowScrollHeight, setWindowScrollHeight] = useState<number>(0); let throttling = false; function onScroll() { if (throttling) return; throttling = true; setTimeout(() => { setWindowScrollHeight(window.scrollY); throttling = false; }, 200); } useEffect(() => { setWindowScrollHeight(window.scrollY); window.addEventListener('scroll', onScroll, { passive: true }); return () => { window.removeEventListener('scroll', onScroll); }; }, []); return windowScrollHeight; }
์ฃผ์ ์ฝ๋๋ ์๋์ ๊ฐ์ต๋๋ค.
let throttling = false; function onScroll() { if (throttling) return; throttling = true; setTimeout(() => { setWindowScrollHeight(window.scrollY); throttling = false; }, 200); }
- throttling ๊ฐ์ด true์ผ ๋๋ ์ด๋ฒคํธ๋ฅผ ์คํํ์ง ์๋๋ค.
- 0.2์ด ๊ฐ๊ฒฉ์ผ๋ก throttling๊ฐ์ด ๋ฐ๋๋ค.
- ๊ทธ๋์ 0.2์ด ๋ง๋ค ์ด๋ฒคํธ๊ฐ ํธ์ถ๋๋ค.
ย
Throttle โ Debounce
์๋ ํ๋ฉด์ ์คํฌ๋กค ํ ๋๋ง๋ค 0.3์ด์ฉ ์ด๋ฒคํธ๋ฅผ ํธ์ถํ๊ณ ์์ต๋๋ค. ๊ทธ๋ฐ๋ฐ ๋ง์ฝ ์ฐ๋กํ๋ง์ ๋๋ฐ์ด์ค๋ก ๋ฐ๊พธ๊ฒ ๋๋ค๋ฉด ์ด๋ป๊ฒ ๋ ๊น์?
๋๋ฐ์ด์ค๋ฅผ ์ ์ฉํ๋ค๋ฉด ์คํฌ๋กค์ ํ๊ณ ์คํฌ๋กค์ด ๋ฉ์ถ ๋ ํธ์ถํ๊ธฐ ๋๋ฌธ์, ์คํฌ๋กค ํ๋ ๊ณผ์ ์์ ์ ๋๋ฉ์ด์
์ ๋ณด์ง ๋ชปํ ๊ฒ์
๋๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์ค๊ฐ ์ค๊ฐ ์ด๋ฒคํธ๋ฅผ ํธ์ถํด์ผ ๋๋ ์ํฉ(ex. ์คํฌ๋กค ์ด๋ฒคํธ)์์๋ ์ฐ๋กํ๋ง์ ์ฌ์ฉํ๋๊ฒ ์ข์ ๊ฒ ์
๋๋ค.
ย
๋ง๋ฌด๋ฆฌ
Debounce์ throttle๋ฅผ ํ๋ก์ ํธ์ ์ ์ฉ์ํจ๋ค๋ฉด ์ฌ์ํ์ง๋ง ํ๋ก์ ํธ์ ์ฑ๋ฅ์ ๋์ด๊ณ ์ฌ์ฉ์ ๊ฒฝํ์ ํฅ์์ํฌ ์ ์์ ๊ฒ์
๋๋ค. ๋ ๊ธฐ๋ฒ ์ค ์ด๋ ํ ๊ฒ์ ์ ์ฉ์ํค๋๊ฒ ์ข์ ์ง ๊ณ ๋ฏผํด๋ณด์๊ณ ํด๋น ๊ธ์ด ๋์๋์
จ๊ธธ ๋ฐ๋๋๋ค!
ย