12.1. useDeferredValue๋?
12.1.1. useDeferredValue ๊ธฐ๋ณธ ์ค๋ช
useDeferredValue๋ React v18.0์ ์๋ก ๋์จ Hook์ผ๋ก, ๋ฆฌ๋ ๋๋ง์ ์ฐ์ ์์์ ๋ฐ๋ผ ์๋์ ์ผ๋ก ๋ ๋๋ง์ ์ง์ฐํ ์ ์๋ Hook์
๋๋ค. useDeferredValue๋ ๊ฐ์ ์
๋ฐ์ดํธ ์ฐ์ ์์๋ฅผ ์ง์ ํ์ฌ ์ฐ์ ์์๊ฐ ๋์ ์์
์ ์คํํ๋ ๋์ useMemo์ ๊ฐ์ด ๊ธฐ์กด์ ๊ฐ์ ๊ฐ์ง๊ณ ์์ผ๋ฉฐ ์
๋ฐ์ดํธ๋ฅผ ์ง์ฐ์ํต๋๋ค.
์ด๋ฌํ ๊ธฐ๋ฅ์ ์ดํ ์ ์์ ๋ค๋ฃฐ debounce, throttle๊ณผ ๋น์ทํด ๋ณด์ด์ง๋ง useDeferredValue Hook์ ์ด์ฉํ๋ฉด ๋๋ ์ด ์๊ฐ์ ๊ณ ์ ํ์ง ์๊ณ ๋ ๊ธด๊ธํ ์์ฒญ์ด ๋๋ ํ ๋ฐ๋ก ์ง์ฐ๋ ๋ ๋๋ง์ ์คํํ๋ค๋ ์ด์ ์ด ์์ต๋๋ค.
const deferredValue = useDeferredValue(value);
useDeferredValue๋ ์ฒซ ๋ฒ์งธ ์ธ์๋ก ์ง์ฐํ๊ธธ ์ํ๋ ๊ฐ์ ๋ฐ๊ณ ํด๋น ๊ฐ์ ๋ณต์ฌ๋ณธ์ ๋ฐํํฉ๋๋ค.
12.1.2 useDeferredValue ์ฌ์ฉ๋ฒ
๋ธ๋ผ์ฐ์ ์์ ๋ ์ฐ์ ์ ์ผ๋ก ์ถ๋ ฅ๋์ด์ผ ํ ๊ฐ์ ๊ณ์ฐํ๋ ๋์ useDeferredValue๋ฅผ ์ด์ฉํ์ฌ DOM ํธ๋ฆฌ์์ ๊ธด๊ธํ์ง ์์ ๋ถ๋ถ์ ๋ฆฌ๋ ๋๋ง์ ์๋์ ์ผ๋ก ์ง์ฐ์ํฌ ์ ์์ต๋๋ค.
์๋ฅผ ๋ค์ด ํ์ฌ ์คํ๋๋ ๋ ๋๋ง์ด input์์์ ๊ฐ์ด ํ์ดํ ์ฆ์ ํ๋ฉด์ ๋ฐ์๋์ด์ผ ํ๋ ๋ ๋๋ง์ด๋ผ๋ฉด React์์๋ ์ด๋ฌํ ๊ธด๊ธํ ๋ ๋๋ง์ ์๋ฃํ๊ธฐ ์ ๊น์ง๋ ์ง์ฐ๋ ๊ฐ์ ๊ธฐ์กด์ ๊ฐ์ผ๋ก ์ถ๋ ฅํ๋ค๊ฐ, ๊ธด๊ธํ ๋ ๋๋ง์ด ๋๋๋ฉด ์๋ก์ด ๊ฐ์ ์ถ๋ ฅํ๋ ๊ฒ์
๋๋ค.
๊ฐ๋จํ ์์ ๋ฅผ ํตํด useDeferredValue ์ฌ์ฉ๋ฒ์ ์์๋ณด๊ฒ ์ต๋๋ค.
import React, { useState } from 'react'; export default function App() { let heavyArray = new Array(10000).fill(0); const [type, setType] = useState(0); const typeChange = (e) => { setType(e.target.value); }; return ( <div> <input type="text" onChange={typeChange} /> { heavyArray.map(() => { return <div>{type}</div>; })} </div>); }
์ ์ฝ๋๋ input์ ํ
์คํธ๋ฅผ ์
๋ ฅํ๋ฉด ๊ฐ์ ๋ด์ฉ์ด 10000๋ฒ ์ถ๋ ฅ๋๋ ์ฝ๋์
๋๋ค. ๊ฐ๋จํ ์ฝ๋์ง๋ง ์ฌ์ฉ์๊ฐ ์
๋ ฅํ ๋ชจ๋ ๊ฐ์ ๋ํด 10000๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌ๋ ๋๋งํ๋ฉด์ ์ฑ๋ฅ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
์ด ๋ useDeferredValue๋ฅผ ์ด์ฉํ๋ค๋ฉด
heavyArray
์ ์ถ๋ ฅ์ ์ง์ฐ์ํฌ ์ ์์ต๋๋ค.import React, { useState, useDeferredValue } from 'react'; export default function App() { let heavyArray = new Array(10000).fill(0); const [type, setType] = useState(0); let deferredType = useDeferredValue(type); const typeChange = (e) => { setType(e.target.value); }; return ( <div> <input type="text" onChange={typeChange} /> { heavyArray.map(() => { return <div>{deferredType}</div>; })} </div>); }
๊ฐ์ ์ฝ๋์์ useDeferredValue๋ฅผ ์ฌ์ฉํ
deferredType
๊ฐ์ ๋ฃ์ด์ฃผ๋ฉด type
๊ฐ์ด ๋ณ๊ฒฝ๋๋ ์์ ์ด ์ง์ฐ๋์ด ํด๋น ๊ฐ์ ์ฌ์ฉํ์ฌ ์ถ๋ ฅ๋๋ ์์๋ค์ ๋ ๋๋ง์ ํ์ ์ฒ๋ฆฌ๋ ์ ์์ด ์ฑ๋ฅ ๊ฐ์ ์ ๋์์ ์ค ์ ์์ต๋๋ค.์ด์ฒ๋ผ useDeferredValue๋ฅผ ์ด์ฉํ๋ฉด ๊ธด๊ธํ ๋ ๋๋ง์ ์ฐ์ ์ ์ผ๋ก ์คํํ๋๋ก ์ํ๋ ๊ฐ์ ๋ ๋๋ง์ ์ง์ฐ์ํฌ ์ ์์ต๋๋ค. ํ์ง๋ง useDeferredValue๋ ๊ฐ์ ์ฐ์ ์์๋ง์ ์ง์ ํ๊ธฐ ๋๋ฌธ์ ์์ ์ปดํฌ๋ํธ์ ๊ฐ์ด๋ ์ํ์ ์
๋ฐ์ดํธ๋ฅผ ์ง์ฐ์ํค๊ธฐ ์ํด์๋ useMemo ํน์ React.memo๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค. ์ด์ ๋ํด์๋ ์ดํ 12.4์์ ์ดํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
์ด์ด์ง๋ ๋ด์ฉ์ผ๋ก๋ React v18.0 ์ถ์ ์ด์ ์ ์๋์ ์ผ๋ก ๋ ๋๋ง์ ์ง์ฐํ๊ธฐ ์ํด ์ฌ์ฉํ๋ ๊ธฐ๋ฒ์ธ debounce์ throttle๋ฅผ ๋จผ์ ์ดํด๋ณธ ๋ค, useDeferredValue Hook์ ๋ํด ์ง์ ์์ ์ฝ๋๋ก ์ดํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
12.2 debounce
12.2.1 debounce๋?
์คํฌ๋กค ์ด๋ฒคํธ ๋๋ ํค๋ณด๋ ์
๋ ฅ๊ณผ ๊ฐ์ ์ฐ์ด์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ ๊ฒฝ์ฐ, ๋ชจ๋ ์ด๋ฒคํธ์ ๋ํด ์ฝ๋ฐฑํจ์๊ฐ ๋์ํ๋ ๊ฒ์ด ์๋ ๋ง์ง๋ง ์ด๋ฒคํธ ๋ฐ์ ์์ ์ผ๋ก๋ถํฐ
setTimeout
์ผ๋ก ์ค์ ํ ์๊ฐ๋งํผ ๊ธฐ๋ค๋ฆฌ๋ค๊ฐ ์ฝ๋ฐฑํจ์๊ฐ ๋์ํ ์ ์๋๋ก ํ๋ ๊ธฐ๋ฅ์ ๊ตฌํํ ๋ ์ฌ์ฉ๋ฉ๋๋ค. ๋์ฒด๋ก ๋ฌดํ ์คํฌ๋กค ๋๋ ๊ฒ์์ด ์
๋ ฅ๊ณผ ๊ฐ์ ๊ธฐ๋ฅ์ ๊ตฌํํ ๋ ์ฃผ๋ก ์ฌ์ฉ๋ฉ๋๋ค. ์๋ ์์ ์ฝ๋๋ฅผ ํตํด ๋ณด๋ค ์์ธํ ์ดํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.12.2.2 debounce ์ฌ์ฉํด๋ณด๊ธฐ
๋ค์์ ์
๋ ฅํ ์ซ์์ ๋ฐฐ์๋ฅผ ์ฐพ์์ฃผ๋ ์์๋ฅผ ๊ตฌํํ ๊ฒ์
๋๋ค.
import { useState, useEffect } from "react"; function App() { const [value, setValue] = useState(""); const [searchResult, setSearchResult] = useState([]); const findNumber = (value) => { const numberGenerater = Array(10000) .fill() .map((value, index) => index + 1); setSearchResult(numberGenerater.filter((num) => !(num % parseInt(value)))); }; useEffect(() => { if (value) { findNumber(value); } else { setSearchResult([]); } }, [value]); return ( <div> <p>์ ๋ ฅํ ์ซ์์ ๋ฐฐ์๋ฅผ ์ฐพ์๋ณด์์~๐</p> <form action="" onSubmit={(e) => e.preventDefault()}> <label htmlFor="">์ ๋ ฅ</label> <input type="number" placeholder="์ซ์๋ฅผ ์ ๋ ฅํ์ธ์" value={value} onChange={(e) => setValue(e.target.value)} /> </form> <ul style={{ listStyle: "none" }}> ๊ฒ์ ๊ฒฐ๊ณผ {searchResult.map((num, index) => ( <li key={index}>{num}</li> ))} </ul> </div> ); } export default App;
์ ์์ ์ ๋ํ์ฌ ๊ฐ๋ตํ ์ค๋ช
ํ๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- input ์์์ ๊ฐ์ ๋ฃ์ผ๋ฉด onChange์ ์ํ์ฌ
value
๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋คsetValue
๋ฅผ ํตํ์ฌvalue
๊ฐ์ด ๋ณ๊ฒฝ๋ฉ๋๋ค.
value
๊ฐ์ด ๋ณ๋๋จ์ ๋ฐ๋ผ ๊ฐ์ํ๊ณ ์๋ useEffect๊ฐ ๋์ํ๊ฒ ๋๊ณ ,value
์ ๊ฐ์ด ์์ ๊ฒฝ์ฐfindNumber
ํจ์๊ฐ ๋์ํ๊ฒ ๋ฉ๋๋ค.
findNumber
ํจ์๊ฐ ๋์ํจ์ ๋ฐ๋ผ 10,000๊ฐ์ ๊ฐ์ด ๋ค์ด์๋ ๋ฐฐ์ด์ด ์์ฑ๋๊ณ , ๊ทธ ์คvalue
์ ๋ฐฐ์์ธ ๊ฐ๋ค๋ง โ๊ฒ์๊ฒฐ๊ณผโ์ ๋ํ๋๊ฒ ๋ฉ๋๋ค.
์ซ์๋ฅผ input์ ์
๋ ฅํ๋ฉด, ๊ฐ์ด ์
๋ ฅ๋ ๋๋ง๋ค
findNumber
ํจ์๊ฐ ๋์ํ๊ฒ ๋จ์ ํ์ธํ ์ ์์ต๋๋ค. ์๋์ ๊ทธ๋ฆผ์ ๊ทธ์ ๋ํ ์์์
๋๋ค.



์ด์ debounce๋ฅผ ์ด์ฉํ ์์ ๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
import { useState, useEffect } from "react"; function App() { const [value, setValue] = useState(""); const [searchResult, setSearchResult] = useState([]); const findNumber = (value) => { const numberGenerater = Array(1000) .fill() .map((value, index) => index + 1); setSearchResult(numberGenerater.filter((num) => !(num % parseInt(value)))); }; useEffect(() => { const debounce = setTimeout(() => { return value ? findNumber(value) : setSearchResult([]); }, 500); return () => clearTimeout(debounce); }, [value]); return ( <div> <p>์ ๋ ฅํ ์ซ์์ ๋ฐฐ์๋ฅผ ์ฐพ์๋ณด์์~</p> <form action="" onSubmit={(e) => e.preventDefault()}> <label htmlFor="">์ ๋ ฅ</label> <input type="number" placeholder="์ซ์๋ฅผ ์ ๋ ฅํ์ธ์" value={value} onChange={(e) => setValue(e.target.value)} /> </form> <ul style={{ listStyle: "none" }}> ๊ฒ์ ๊ฒฐ๊ณผ {searchResult.map((num, index) => ( <li key={index}>{num}</li> ))} </ul> </div> ); } export default App;
๋ณ๊ฒฝ๋ ๋ถ๋ถ์ useEffect ๋ถ๋ถ์
๋๋ค. ์ค์ ์๊ฐ์ธ 500ms ๋์
findNumber
ํจ์ ๋์์ ์ง์ฐ์ ์ธ์์ ์ผ๋ก ์ ํํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ 500ms๊ฐ ์ง๋๋ฉด debounce์ setTimeout
์ฝ๋ฐฑํจ์๋ฅผ ์ด๊ธฐํ ์์ผ์ค๋๋ค. ์ด๋ฅผ ํตํด debounce๋ฅผ ๊ณ ๋ คํ์ง ์์์ ๋์ ๊ฐ์ด โ3421โ์ input์ ์
๋ ฅํ์์ ๋ ๋งค ์
๋ ฅ์ ๋ํด ๊ฒ์ ๊ฒฐ๊ณผ๊ฐ ๋ํ๋๋ ๊ฒ์ด ์๋๋ผ โ3421โ์ ๋ชจ๋ ์
๋ ฅํ๊ณ 500ms๊ฐ ์ง๋ฌ์ ๋์๋ง, ๊ฒ์ ๊ฒฐ๊ณผ๊ฐ ๋ํ๋๊ฒ ๋จ์ ํ์ธํ ์ ์์ต๋๋ค.
์ฆ, debounce๋ฅผ ์ด์ฉํ์ฌ ์ค์ ํ ์๊ฐ๋งํผ ํจ์์ ๋์์ ์ง์ฐ์ํฌ ์ ์๊ณ , ์ด๋ฅผ ํตํด ๋ถํ์ํ ํจ์ ๋์์ ๋ฐฉ์งํ ์ ์์ต๋๋ค.
12.3 throttle
12.3.1 throttle์ด๋?
throttle์ ์ด๋ฒคํธ๋ฅผ ์ผ์ ํ ์ฃผ๊ธฐ๋ง๋ค ๋ฐ์ํ๋๋ก ํ๋ ๊ธฐ๋ฒ์
๋๋ค. callback ํจ์๊ฐ ์ค์ ํ time ๋ค์ ์คํ๋๊ณ , time์ด ์ง๋๊ธฐ ์ ์ ๋ค์ ํธ์ถ๋ ๊ฒฝ์ฐ์๋ callback์ ์คํ์ํค์ง ์๊ณ ํจ์๋ฅผ ์ข
๋ฃํ๋ ํํ๋ก ๋์ด ์์ต๋๋ค. ๋ง์ฝ, ์ค์ ์๊ฐ์ 200ms๋ก ์ค์ ํ๋ฉด ํด๋น ์ด๋ฒคํธ๋ 200ms ๋์ ์ต๋ ํ ๋ฒ๋ง ๋ฐ์ํฉ๋๋ค.
์ด๋ฒคํธ์ ํธ์ถ์ ์ค์ ํ ์๊ฐ์ ๊ธฐ์ค์ผ๋ก ์ผ์ ํ๊ฒ ์คํํ๋๋ก ์ ํ์ ๋๋ฉด ๊ณผ๋ํ ์ด๋ฒคํธ ํธ๋ค๋ฌ์ ์คํ๋๋ ๋น๋๋ฅผ ์ค์ฌ ์น ์ฑ๋ฅ์ด ์ ํ๋๋ ๊ฒ์ ๋ฐฉ์งํ ์ ์๋ ์ฑ๋ฅ์ ์ธ ์ด์ ์ ๊ฐ์ ธ๊ฐ ์ ์์ต๋๋ค.
12.3.2 throttle ์ฌ์ฉํด๋ณด๊ธฐ
12.3.1์ ์์ throttle์ ๊ฐ๋
์ ๋ํด ์ดํด๋ณด์์ต๋๋ค. ์ง๊ธ๋ถํฐ๋ ์์ ์ฝ๋๋ฅผ ํตํด ์ผ๋ฐ ์ด๋ฒคํธ์ throttle์ ์ ์ฉํ ์ด๋ฒคํธ๊ฐ ์ด๋ป๊ฒ ๋ค๋ฅด๊ฒ ๋์ํ๋์ง ์ดํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค. ์๋๋ ํธ๋ฆฌ๊ฑฐ ๋ฐ์ค ์์ ์์ ๋ง์ฐ์ค๊ฐ ์๋ ๋์ ์ด๋ํ ๋๋ง๋ค ๊ฐ๊ฐ
raw
์ํ์ throttled
์ํ๊ฐ 1์ฉ ์ฆ๊ฐํ๋ ์์ ์ฝ๋์
๋๋ค.import { useCallback, useRef, useState } from 'react'; const App = () => { const [raw, setRaw] = useState(0); const [throttled, setThrottled] = useState(0); const lastRan = useRef(Date.now()); const handleOnMouseMove = useCallback(() => { setRaw((r) => r + 1); if (Date.now() - lastRan.current >= 1000) { setThrottled((t) => t + 1); lastRan.current = Date.now(); } }, []); return ( <div> <div style={{ width: '100px', height: '100px', backgroundColor: 'rosybrown', }} onMouseMove={handleOnMouseMove} > ํธ๋ฆฌ๊ฑฐ ์์ญ </div> <p>์ผ๋ฐ ์ด๋ฒคํธ: {raw}</p> <p>์ฐ๋กํ ์ด๋ฒคํธ: {throttled}</p> </div> ); }; export default App;

์์ ์ฝ๋์์ ๋ ์ํ๋ ์ด๊ธฐ ์ํ๋ก ๊ฐ์ 0์ผ๋ก ์์ํฉ๋๋ค.
๋จผ์ , ์ผ๋ฐ ์ด๋ฒคํธ์ ๋ํด ์ดํด๋ณด๊ฒ ์ต๋๋ค.
raw
์ํ๋ ํธ๋ฆฌ๊ฑฐ ์์ญ ๋ฐ์ค ์์์ ๋ง์ฐ์ค๊ฐ ์์ง์ผ ๋๋ง๋ค handleOnMouseMove
์ฝ๋ฐฑํจ์๊ฐ ์คํ๋ฉ๋๋ค. ๊ทธ ๊ฒฐ๊ณผ๋ก 261๊น์ง ๋ณํํ ๊ฒ์ ํ์ธํ ์ ์๊ณ , ์ด๋ App ์ปดํฌ๋ํธ๊ฐ 261๋ฒ ๋ฆฌ๋ ๋๋ง๋๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค. ๋ง์ฝ ์ด๋ฒคํธ ํธ๋ค๋ฌ๊ฐ ๋ฌด๊ฑฐ์ด ๊ณ์ฐ์ด๋ ๊ธฐํ DOM ์กฐ์๊ณผ ๊ฐ์ ์์
์ ๊ณผํ๊ฒ ์ํํ๋ ๊ฒฝ์ฐ ์ด๋ ์ฑ๋ฅ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๊ณ ์ฌ์ฉ์ ๊ฒฝํ๊น์ง ๋จ์ด๋จ๋ฆด ์ ์์ต๋๋ค.if (Date.now() - lastRan.current >= 1000) { setThrottled((t) => t + 1); lastRan.current = Date.now(); }
๋ค์์ผ๋ก throttle์ ์ ์ฉํ ์ด๋ฒคํธ์ ๋ํด ์ดํด๋ณด๊ฒ ์ต๋๋ค.
throttled
์ํ๋ ๋ง์ฐฌ๊ฐ์ง๋ก ํธ๋ฆฌ๊ฑฐ ์์ญ ๋ฐ์ค ์์์ ๋ง์ฐ์ค๊ฐ ์์ง์ผ ๋๋ง๋ค handleOnMouseMove
์ฝ๋ฐฑํจ์๊ฐ ์คํ๋ฉ๋๋ค. ํ์ง๋ง, raw
์ํ์๋ ๋ค๋ฅด๊ฒ ์ ์ฝ๋๋ฅผ ์์ฑํ์ฌ if ๋ฌธ์์ 1000ms๋ง๋ค ํ ๋ฒ์ฉ ์คํ๋๋๋ก ์ค์ ํ์ต๋๋ค. ๋ฐ๋ผ์ raw
์ํ๊ฐ 261๋ก ์
๋ฐ์ดํธ ๋๋ ๋์ throttled
์ํ๋ 3์ผ๋ก ์
๋ฐ์ดํธ ๋์ด ๊ณผ๋ํ๊ฒ ๋ฆฌ๋ ๋๋ง ๋๋ ๊ฒ์ ๋ฐฉ์งํ ์ ์์ต๋๋ค.12.4 useDeferredValue ์ฌ์ฉํด๋ณด๊ธฐ
ํค๋ณด๋๋ฅผ ํ์ฉํ ์ฌ์ฉ์ ์
๋ ฅ ๋ฑ ์งง์ ์๊ฐ ๋์ ์ด๋ฒคํธ๊ฐ ๋ง์ด ์ผ์ด๋๋ ๊ฒฝ์ฐ ํ๋ฉด์ด ๋๊ธฐ๋ ํ์์ด ์ผ์ด๋ ์ ์์ต๋๋ค. ๊ฒ์์ด ์๋ ์์ฑ์ ๊ทธ ์๋ก ๋ค ์ ์๋๋ฐ, ์ฌ์ฉ์ ์
๋ ฅ ํ ๋ฒ์ ๊ฒ์์ด ์
๋ฐ์ดํธ๊ฐ ํ ๋ฒ์ฉ ์ผ์ด๋๋ค๋ฉด ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง์ด ์ด๋น ์ ๋ฒ์ฉ ๋ฐ์ํ๋ฉฐ, ์ฌ์ฉ์๊ฐ ์ํ๋ ๊ฒ์์ด์ ๋ํ ์๋์์ฑ์ด ๋๋ ค์ง ์ ์์ต๋๋ค.
์ฌ์ฉ์๊ฐ input ์ฐฝ์ ํ
์คํธ๋ฅผ ์
๋ ฅํ๋ฉด ํด๋น ํ
์คํธ๋ฅผ 10000๊ฐ๋ฅผ ๋ฐ๋ณตํ๋ ์์ ๋ฅผ ์์ฑํ๊ณ , ๊ทธ ์ฑ๋ฅ์ ์ธก์ ํด๋ณด๊ฒ ์ต๋๋ค.
.app { margin: 20px auto; width: 300px; } .input { width: 200px; font-size: 20px; background-color: sandybrown; border-radius: 5px; } .search-div { width: 200px; font-size: 15px; padding: 3px; border: 0.1px solid black; border-radius: 5px; }
import React, { useState, useCallback } from 'react'; import './App.css'; export default function App() { const [name, setName] = useState(''); const onChange = useCallback((e) => { setName(e.target.value); }, []); return ( <div className='app'> <div>๊ฒ์์ฐฝ</div> <input className='input' value={name} onChange={onChange} /> {name ? Array(10000) .fill() .map((v, i) => ( <div className='search-div' key={i}> {name} </div> )) : null} </div> ); }


์ฌ์ฉ์๋ ์ ๊ฒ์์ฐฝ์ โHello worldโ ๋ผ๋ ํ
์คํธ๋ฅผ ์
๋ ฅํ๋ ๋์ ์ง๊ด์ ์ผ๋ก๋ ํ๋ฉด์ ๋๊น ํ์์ ๋ง์ฃผํ ์ ์์ต๋๋ค. 10000๊ฐ์ ํ
์คํธ๋ฅผ ๋ฐ๋ณตํ๋ ๋์, ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง์ด ๋ฐ์ํ๊ธฐ ๋๋ฌธ์
๋๋ค. Chrome์์ ์ ๊ณตํ๋ ๊ฐ๋ฐ์ ๋๊ตฌ์ Perfomance ํญ์์ โHello worldโ ํ
์คํธ๋ฅผ ๋ชจ๋ ๋ ๋๋งํ๋ ๋ฐ ๊ฑธ๋ฆฐ ์๊ฐ์ ํ์ธํ ์ ์์ต๋๋ค. 2000ms๋์ โHello worldโ ํ
์คํธ๊ฐ ๋ชจ๋ ๋ ๋๋ง ๋๋ ๋ฐ ๊ฑธ๋ฆฐ ์๊ฐ์ 586ms์
๋๋ค.
๊ทธ๋ ๋ค๋ฉด ์ด๋ฒ์ useDefferedValue Hook์ ์ฌ์ฉํ์ฌ ๊ฒ์์ฐฝ์ ์ฑ๋ฅ์ ์ธก์ ํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
import React, { useState, useCallback, useMemo, useDeferredValue } from 'react'; import './App.css'; export default function App() { const [name, setName] = useState(''); const deferredName = useDeferredValue(name); const result = useMemo(() => deferredName, [deferredName]); const onChange = useCallback((e) => { setName(e.target.value); }, []); return ( <div className='app'> <div>๊ฒ์์ฐฝ</div> <input className='input' value={name} onChange={onChange} /> {deferredName ? Array(10000) .fill() .map((v, i) => ( <div className='search-div' key={i}> {result} </div> )) : null} </div> ); }
์ App.jsx์์๋ useMemo Hook์ ์ฌ์ฉํ์์ต๋๋ค.
name
์ด ๋ณ๊ฒฝ๋ ๋๊ฐ ์๋, deferredName
์ด ๋ณ๊ฒฝ๋ ๋๋ง ๋ฆฌ๋ ๋๋ง์ ์งํํ๋ฉด์ ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง์ ๋ง์ ์ ์์ต๋๋ค. useDeferredValue ์ฌ์ฉ ์์ useMemo๋ฅผ ํจ๊ป ์ฌ์ฉํ๋ฉด์ ํ์ ์ปดํฌ๋ํธ๋ ์ํ์ ์
๋ฐ์ดํธ๋ฅผ ์ง์ฐ์ํฌ ์ ์์ต๋๋ค.
์ด๋ฒ์๋ โHello worldโ ๋ผ๋ ํ
์คํธ๋ฅผ ์
๋ ฅํ๋ ๋์, 10000๊ฐ์ ๊ฒ์ ๊ฒฐ๊ณผ๊ฐ ์ถ๋ ฅ๋๋ ๋ฐ ๋๊น ํ์์ด ํจ์ฌ ์ํ๋์์์ ์ฒด๊ฐํ ์ ์์ต๋๋ค. ๋ํ ๊ฐ๋ฐ์ ๋๊ตฌ์์ ์ด 2000ms๋์ ๋ ๋๋ง ํ๋๋ฐ ๊ฑธ๋ฆฐ ์๊ฐ์ 376ms๋ก, useDefferedValue๋ฅผ ์ฌ์ฉํ์ง ์์์ ๋๋ณด๋ค ์ฝ 1.5๋ฐฐ ๋น ๋ฅธ ์ฑ๋ฅ์ ๋ณด์ฌ์ค๋๋ค.
๋ถํ์ํ ๋ฆฌ๋ ๋๋ง์ ๋ง๊ธฐ ์ํด debounce๋ throttle ๊ฐ์ ์ฒ๋ฆฌ๋ฅผ ์งํํ๋๋ฐ, debounce์ throttle์์ ์ ์ ํ delay๋ฅผ ์ ํํ๋ ๊ฒ์ ๊ฝค๋ ์ด๋ ค์ด ์ผ์
๋๋ค.
React v18.0์ fiber๋ผ๋ ์์ง์ ๊ฐ์ ํ์ฌ ์์ฒด์ ์ธ ์ค์ผ์ค๋ฌ๋ฅผ ๊ฐ์ง๊ฒ ๋์๊ณ , ์์
์ ์ฐ์ ์์๋ฅผ ์ ํ์ฌ ์์ฐจ์ ์ผ๋ก ์ฒ๋ฆฌํ๋ ๊ธฐ๋ฅ์ ๋ดํฌํ๊ฒ ๋์์ต๋๋ค. ๋ฎ์ ์ฐ์ ์์๋ฅผ ์ง์ ํ๊ธฐ ์ํ useDeferredValue๋ผ๋ ๋นํธ์ธ ํ
์ ์ฌ์ฉํ์ฌ ์์ ์์ ๋ฅผ ๋ค๋ค๋ณด๊ณ , ์ฑ๋ฅ์ ํ์ธํ์์ต๋๋ค.