React 18 useDeferredValue ์‹ฌํ™”: ๊ฒ€์ƒ‰ ์ž๋™์™„์„ฑ์—์„œ ๋””๋ฐ”์šด์‹ฑ์„ ์ œ๊ฑฐํ•˜๋Š” ์ด์œ 

React 18 useDeferredValue ์‹ฌํ™”: ๊ฒ€์ƒ‰ ์ž๋™์™„์„ฑ์—์„œ ๋””๋ฐ”์šด์‹ฑ์„ ์ œ๊ฑฐํ•˜๋Š” ์ด์œ 

๐ŸŽฃ React ์ฐธ์กฐ ๋ฌด๊ฒฐ์„ฑ ์ •๋ณต: useMemo์™€ useCallback์˜ ์™„๋ฒฝํ•œ ์‚ฌ์šฉ ๊ฐ€์ด๋“œ

๐Ÿค”  ์™œ ๋‚ด useEffect๋Š” ๋ฉˆ์ถ”์ง€ ์•Š์„๊นŒ?

React ๊ฐœ๋ฐœ์„ ํ•˜๋‹ค ๋ณด๋ฉด, ๋ถ„๋ช…ํžˆ ์ฝ”๋“œ๋ฅผ ๋งž๊ฒŒ ์งฐ๋Š”๋ฐ๋„ useEffect๊ฐ€ ๋ฌดํ•œํžˆ ์‹คํ–‰๋˜๊ฑฐ๋‚˜, ์ž์‹ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ด์œ  ์—†์ด ๋ฆฌ๋ Œ๋”๋ง ๋˜๋Š” ๊ฒฝํ—˜์„ ํ•ด๋ณด์…จ์„ ๊ฒ๋‹ˆ๋‹ค.
๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ, ๋ฒ”์ธ์€ ๋กœ์ง์˜ ์˜ค๋ฅ˜๊ฐ€ ์•„๋‹ˆ๋ผ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ์ฐธ์กฐ ๋ฌด๊ฒฐ์„ฑ(Referential Integrity)์ด ๊นจ์กŒ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
React์—์„œ ๊ฐ์ฒด๋‚˜ ํ•จ์ˆ˜๋Š” ๋งค ๋ Œ๋”๋ง๋งˆ๋‹ค ์ƒˆ๋กœ์šด ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๋ฅผ ํ• ๋‹น๋ฐ›๋Š”๋ฐ, React๋Š” ์ด๋ฅผ '๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ๋‹ค'๊ณ  ์ธ์‹ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
์˜ค๋Š˜์€ React ์„ฑ๋Šฅ ์ตœ์ ํ™”์˜ ์–‘๋‚ ์˜ ๊ฒ€์ธ useMemo์™€ useCallback์„ ํ†ตํ•ด ์ด ์ฐธ์กฐ ๋ฌด๊ฒฐ์„ฑ์„ ์–ด๋–ป๊ฒŒ ์ง€์ผœ๋‚ด๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  ์˜์กด์„ฑ ๋ฐฐ์—ด(Dependency Array)์„ ์–ด๋–ป๊ฒŒ ๊ด€๋ฆฌํ•ด์•ผ ๋ฒ„๊ทธ๋ฅผ ๋ง‰์„ ์ˆ˜ ์žˆ๋Š”์ง€ ๊นŠ์ด ํŒŒํ—ค์ณ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๐Ÿ’ก ํ•ต์‹ฌ ๊ฐœ๋…: JavaScript์—์„œ {} === {}๋Š” false์ž…๋‹ˆ๋‹ค. ๋ชจ์–‘์ด ๊ฐ™์•„๋„ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๊ฐ€ ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด React ์ตœ์ ํ™”์˜ ์‹œ์ž‘์ ์ž…๋‹ˆ๋‹ค.

๐Ÿ” 1. ์ฐธ์กฐ ๋™๋“ฑ์„ฑ(Referential Equality) ์ดํ•ดํ•˜๊ธฐ

Hook์„ ๋ฐฐ์šฐ๊ธฐ ์ „์—, React๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ๋น„๊ตํ•˜๋Š” ๋ฐฉ์‹์„ ๋จผ์ € ์ดํ•ดํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
React๋Š” ๋ Œ๋”๋ง ์ „ํ›„์˜ Props๋‚˜ State๋ฅผ ๋น„๊ตํ•  ๋•Œ ์–•์€ ๋น„๊ต(Shallow Compare)๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

๋ฐ์ดํ„ฐ ํƒ€์ž… ๋น„๊ต ๋ฐฉ์‹ ๊ฒฐ๊ณผ ์˜ˆ์‹œ
์›์‹œ ํƒ€์ž… (Primitive) ๊ฐ’(Value) ์ž์ฒด๋ฅผ ๋น„๊ต 1 === 1 (True)
์ฐธ์กฐ ํƒ€์ž… (Object, Array, Function) ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ(Reference)๋ฅผ ๋น„๊ต [] === [] (False)

๋ฌธ์ œ๋Š” ์ปดํฌ๋„ŒํŠธ ํ•จ์ˆ˜๊ฐ€ ์žฌ์‹คํ–‰๋  ๋•Œ๋งˆ๋‹ค, ํ•จ์ˆ˜ ๋‚ด๋ถ€์— ์„ ์–ธ๋œ ๊ฐ์ฒด์™€ ํ•จ์ˆ˜๋“ค๋„ ๋งค๋ฒˆ ์ƒˆ๋กœ ์ƒ์„ฑ๋œ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค.
์ด๋กœ ์ธํ•ด ์ž์‹ ์ปดํฌ๋„ŒํŠธ์ธ React.memo๊ฐ€ ์ ์šฉ๋˜์–ด ์žˆ์–ด๋„, Props๋กœ ์ „๋‹ฌ๋˜๋Š” ๊ฐ์ฒด๋‚˜ ํ•จ์ˆ˜์˜ ์ฃผ์†Œ๊ฐ€ ๋ฐ”๋€Œ์–ด ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ› ️ 2. useMemo์™€ useCallback์˜ ์ •ํ™•ํ•œ ์‚ฌ์šฉ๋ฒ•

A. useMemo: ๊ฐ’(Value)๊ณผ ๊ฐ์ฒด์˜ ์ฃผ์†Œ ์ €์žฅ

useMemo๋Š” ๊ณ„์‚ฐ ๋น„์šฉ์ด ๋งŽ์ด ๋“œ๋Š” ํ•จ์ˆ˜์˜ ๋ฆฌํ„ด๊ฐ’์„ ์บ์‹ฑํ•˜๊ฑฐ๋‚˜, ๊ฐ์ฒด/๋ฐฐ์—ด์˜ ์ฐธ์กฐ๋ฅผ ๊ณ ์ •ํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

✅ ์˜ฌ๋ฐ”๋ฅธ ์‚ฌ์šฉ ์˜ˆ์‹œ:
const heavyValue = useMemo(() => calculateHugeData(data), [data]);
const stableObject = useMemo(() => ({ id: 1, text: 'Hello' }), []);

์ด์ฒ˜๋Ÿผ ๊ฐ์ฒด๋ฅผ ๊ฐ์‹ธ์ฃผ๋ฉด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง ๋˜์–ด๋„ stableObject๋Š” ๋™์ผํ•œ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๋ฅผ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

B. useCallback: ํ•จ์ˆ˜(Function)์˜ ์ฃผ์†Œ ์ €์žฅ

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ํ•จ์ˆ˜๋„ ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค. ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์˜ ํ•จ์ˆ˜๋Š” ๋ Œ๋”๋ง๋งˆ๋‹ค ์ƒˆ๋กœ ๋งŒ๋“ค์–ด์ง‘๋‹ˆ๋‹ค.
useCallback์€ ํ•จ์ˆ˜ ์ธ์Šคํ„ด์Šค ์ž์ฒด๋ฅผ ๋ฉ”๋ชจ์ด์ œ์ด์…˜ํ•˜์—ฌ, ์ž์‹ ์ปดํฌ๋„ŒํŠธ์— Props๋กœ ์ „๋‹ฌํ•  ๋•Œ ์ฐธ์กฐ ๋ฌด๊ฒฐ์„ฑ์„ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

⚠️ ์–ธ์ œ ์‚ฌ์šฉํ•ด์•ผ ํ• ๊นŒ์š”?

  • 1. ์ž์‹ ์ปดํฌ๋„ŒํŠธ๊ฐ€ React.memo๋กœ ์ตœ์ ํ™”๋˜์–ด ์žˆ์„ ๋•Œ.
  • 2. ํ•จ์ˆ˜๋‚˜ ๊ฐ์ฒด๊ฐ€ ๋‹ค๋ฅธ Hook(useEffect ๋“ฑ)์˜ ์˜์กด์„ฑ ๋ฐฐ์—ด(Dependency Array)์— ํฌํ•จ๋  ๋•Œ.

๐Ÿ›ก️ 3. ๋””ํŽœ๋˜์‹œ(Dependency) ๊ด€๋ฆฌ์™€ Stale Closure

useMemo์™€ useCallback ์‚ฌ์šฉ ์‹œ ๊ฐ€์žฅ ์ฃผ์˜ํ•ด์•ผ ํ•  ์ ์€ ์˜์กด์„ฑ ๋ฐฐ์—ด(deps) ๊ด€๋ฆฌ์ž…๋‹ˆ๋‹ค.
์˜์กด์„ฑ ๋ฐฐ์—ด์„ ์ž˜๋ชป ์„ค์ •ํ•˜๋ฉด ์ตœ์‹  ์ƒํƒœ๋ฅผ ์ฐธ์กฐํ•˜์ง€ ๋ชปํ•˜๋Š” Stale Closure(์˜ค๋ž˜๋œ ํด๋กœ์ €) ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฑฐ๋‚˜, ๋ฉ”๋ชจ์ด์ œ์ด์…˜์ด ๋ฌด์šฉ์ง€๋ฌผ์ด ๋ฉ๋‹ˆ๋‹ค.

๐Ÿšจ ํ”ํ•œ ์‹ค์ˆ˜: ์˜์กด์„ฑ ์ƒ๋žต

"ํ•จ์ˆ˜๊ฐ€ ๋„ˆ๋ฌด ์ž์ฃผ ๋ฐ”๋€Œ์–ด์„œ ๋ฌดํ•œ ๋ฃจํ”„๊ฐ€ ๋Œ์•„์š”"๋ผ๋ฉฐ ์˜์กด์„ฑ ๋ฐฐ์—ด์—์„œ ํŠน์ • ๋ณ€์ˆ˜๋ฅผ ๊ณ ์˜๋กœ ๋นผ๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
์ด๋Š” React์—๊ฒŒ ๊ฑฐ์ง“๋ง์„ ํ•˜๋Š” ํ–‰์œ„์ด๋ฉฐ, ์‹ฌ๊ฐํ•œ ๋ฒ„๊ทธ๋ฅผ ์œ ๋ฐœํ•ฉ๋‹ˆ๋‹ค.

❌ ์ž˜๋ชป๋œ ์ฝ”๋“œ (Stale Closure ๋ฐœ์ƒ):
const handleClick = useCallback(() => {
  console.log(count); // count๊ฐ€ ๋ฐ”๋€Œ์–ด๋„ ํ•ญ์ƒ 0๋งŒ ์ถœ๋ ฅ๋  ์ˆ˜ ์žˆ์Œ
}, []); // count๋ฅผ ์˜์กด์„ฑ์— ๋„ฃ์ง€ ์•Š์Œ

✅ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• (ํ•จ์ˆ˜ํ˜• ์—…๋ฐ์ดํŠธ):
const handleClick = useCallback(() => {
  setCount(prev => prev + 1); // ์ตœ์‹  ์ƒํƒœ๋ฅผ ๋ณด์žฅ๋ฐ›์Œ
}, []); // ์˜์กด์„ฑ ์—†์ด๋„ ์•ˆ์ „ํ•จ

์ƒํƒœ ๊ฐ’์„ ์ง์ ‘ ์ฐธ์กฐํ•˜๋Š” ๋Œ€์‹  ํ•จ์ˆ˜ํ˜• ์—…๋ฐ์ดํŠธ(Functional Update)๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, ์˜์กด์„ฑ ๋ฐฐ์—ด์„ ๋น„์šฐ๋ฉด์„œ๋„ ํ•ญ์ƒ ์ตœ์‹  ๊ฐ’์„ ์•ˆ์ „ํ•˜๊ฒŒ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


✅ ๊ณผ๋„ํ•œ ์ตœ์ ํ™”๋Š” ๊ธˆ๋ฌผ

์ฐธ์กฐ ๋ฌด๊ฒฐ์„ฑ์„ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์€ ์ค‘์š”ํ•˜์ง€๋งŒ, ๋ชจ๋“  ํ•จ์ˆ˜์™€ ๊ฐ์ฒด์— useMemo์™€ useCallback์„ ๊ฐ์‹ธ๋Š” ๊ฒƒ์€ ์˜คํžˆ๋ ค ์„ฑ๋Šฅ ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๋ฉ”๋ชจ์ด์ œ์ด์…˜ ์ž์ฒด๋„ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ๋น„๊ต ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋น„์šฉ์ด ๋“ค๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๐Ÿš€ ์š”์•ฝ ๋ฐ ์‹ค์ฒœ ๊ฐ€์ด๋“œ

  1. 1. ์›๋ฆฌ ํŒŒ์•…: ๊ฐ์ฒด์™€ ํ•จ์ˆ˜๋Š” ๋ Œ๋”๋ง๋งˆ๋‹ค ์ฐธ์กฐ ์ฃผ์†Œ๊ฐ€ ๋ฐ”๋€๋‹ˆ๋‹ค.
  2. 2. ์ ์ ˆํ•œ ์‚ฌ์šฉ: ์ž์‹ ์ปดํฌ๋„ŒํŠธ์— Props๋กœ ๋„˜๊ธฐ๊ฑฐ๋‚˜, ์˜์กด์„ฑ ๋ฐฐ์—ด์— ๋“ค์–ด๊ฐˆ ๋•Œ๋งŒ ๋ฉ”๋ชจ์ด์ œ์ด์…˜ ํ•˜์‹ญ์‹œ์˜ค.
  3. 3. ์ •์งํ•œ ๋ฐฐ์—ด: ์˜์กด์„ฑ ๋ฐฐ์—ด์—๋Š” ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋“  ์™ธ๋ถ€ ๋ณ€์ˆ˜๋ฅผ ๋„ฃ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. (ESLint ๊ทœ์น™ ์ค€์ˆ˜ ์ถ”์ฒœ)

React Hooks์˜ ์ฐธ์กฐ ๊ด€๋ฆฌ ๋Šฅ๋ ฅ์„ ๋งˆ์Šคํ„ฐํ•œ๋‹ค๋ฉด, ์„ฑ๋Šฅ ๋ฌธ์ œ ์—†์ด ๋ณต์žกํ•˜๊ณ  ๊ฑฐ๋Œ€ํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์•ˆ์ •์ ์œผ๋กœ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋Š” ์ง„์ •ํ•œ React ์ „๋ฌธ๊ฐ€๋กœ ๊ฑฐ๋“ญ๋‚  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋Œ“๊ธ€ ์“ฐ๊ธฐ