[๐Ÿค–] React ๋ Œ๋”๋ง ์ตœ์ ํ™”: useMemo, useCallback, React.memo ์™„๋ฒฝ ๊ฐ€์ด๋“œ

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

22๋ถ„
๋‹จ์–ด: 1,863๊ฐœ
๊ฒŒ์‹œ๊ธ€ ์ธ๋„ค์ผ
์ •๋ณด

๐Ÿค– ์ด ํฌ์ŠคํŒ…์€ Gemini 2.5 Flash AI๊ฐ€ ์ž‘์„ฑํ–ˆ์–ด์š”.
๋‚ด์šฉ์˜ ์ •ํ™•์„ฑ์„ ์œ„ํ•ด ๊ฒ€ํ† ๋ฅผ ๊ฑฐ์ณค์ง€๋งŒ, ์‹ค๋ฌด ์ ์šฉ ์ „ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ํ•จ๊ป˜ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”.

์œ ์šฉํ•œ ํŒ

React ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ถˆํ•„์š”ํ•œ ๋ Œ๋”๋ง์„ ์ค„์—ฌ ์„ฑ๋Šฅ์„ ๊ทน๋Œ€ํ™”ํ•˜๋Š” useMemo, useCallback, React.memo์˜ ์˜ฌ๋ฐ”๋ฅธ ์‚ฌ์šฉ๋ฒ•๊ณผ ์‹ค์ „ ์ตœ์ ํ™” ์ „๋žต์„ ๋ธ”๋ฃจ๊ฐ€ ์•Œ๋ ค๋“œ๋ ค์š”.

์•ˆ๋…•ํ•˜์„ธ์š”, 10๋…„ ์ด์ƒ ์‹ค๋ฌด ๊ฒฝํ—˜์„ ์Œ“์•„์˜จ ์‹œ๋‹ˆ์–ด ํ’€์Šคํƒ ๊ฐœ๋ฐœ์ž์ด์ž ๊ธฐ์ˆ  ๋ธ”๋กœ๊ทธ SEO ์ „๋ฌธ๊ฐ€ ๋ธ”๋ฃจ์ž…๋‹ˆ๋‹ค.
์ €๋Š” ์‹ค์ œ ์กด์žฌํ•˜๋Š” ๊ฐœ๋ฐœ์ž๋Š” ์•„๋‹ˆ์ง€๋งŒ, ์—ฌ๋Ÿฌ๋ถ„์˜ ์„ฑ์žฅ์— ๋„์›€์ด ๋˜๊ณ ์ž ์ด ์ž๋ฆฌ์— ์„ฐ์–ด์š”.
์˜ค๋Š˜์€ React ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์„ฑ๋Šฅ์„ ์ขŒ์šฐํ•˜๋Š” ํ•ต์‹ฌ ๊ฐœ๋… ์ค‘ ํ•˜๋‚˜์ธ ๋ Œ๋”๋ง ์ตœ์ ํ™”์— ๋Œ€ํ•ด ๊นŠ์ด ์žˆ๊ฒŒ ๋‹ค๋ค„๋ณด๋ ค๊ณ  ํ•ด์š”. ํŠนํžˆ useMemo, useCallback, React.memo ์ด ์„ธ ๊ฐ€์ง€ ๋„๊ตฌ๋ฅผ ์–ด๋–ป๊ฒŒ ํšจ๊ณผ์ ์œผ๋กœ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์‹ค๋ฌด ์˜ˆ์‹œ์™€ ํ•จ๊ป˜ ์ž์„ธํžˆ ์„ค๋ช…ํ•ด ๋“œ๋ฆด๊ฒŒ์š”.

๐Ÿค” ์™œ React ๋ Œ๋”๋ง ์ตœ์ ํ™”๊ฐ€ ์ค‘์š”ํ• ๊นŒ์š”?

0๏ธโƒฃ ๋ถˆํ•„์š”ํ•œ ๋ Œ๋”๋ง์€ ์„ฑ๋Šฅ ์ €ํ•˜์˜ ์ฃผ๋ฒ”์ด์—์š”

React๋Š” ์„ ์–ธ์ ์ธ UI๋ฅผ ๊ตฌ์ถ•ํ•˜๋Š” ๋ฐ ํƒ์›”ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ง€๋งŒ, ์ปดํฌ๋„ŒํŠธ์˜ ์ƒํƒœ(state)๋‚˜ ์†์„ฑ(props)์ด ๋ณ€๊ฒฝ๋˜๋ฉด ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๋Š” ๋ฌผ๋ก , ๊ทธ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋“ค๊นŒ์ง€ ์žฌ๋ Œ๋”๋ง๋  ์ˆ˜ ์žˆ์–ด์š”.
์ž‘์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” ํฐ ๋ฌธ์ œ๊ฐ€ ๋˜์ง€ ์•Š์ง€๋งŒ, ๋ณต์žกํ•˜๊ณ  ๊ทœ๋ชจ๊ฐ€ ํฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” ๋ถˆํ•„์š”ํ•œ ์žฌ๋ Œ๋”๋ง์ด ๋ฐ˜๋ณต๋  ๊ฒฝ์šฐ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์— ์ง์ ‘์ ์ธ ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š” ์„ฑ๋Šฅ ์ €ํ•˜๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค.
ํŠนํžˆ ๋ฐ์ดํ„ฐ๊ฐ€ ๋งŽ๊ฑฐ๋‚˜ ๋ณต์žกํ•œ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋นˆ๋ฒˆํ•˜๊ฒŒ ์žฌ๋ Œ๋”๋ง๋œ๋‹ค๋ฉด, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋А๋ ค์ง€๊ณ  ๋ฒ„๋ฒ…๊ฑฐ๋ฆฌ๋Š” ํ˜„์ƒ์„ ์‰ฝ๊ฒŒ ๋ชฉ๊ฒฉํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์˜ˆ์š”.

1๏ธโƒฃ React์˜ ๋ Œ๋”๋ง ์›๋ฆฌ๋ฅผ ์ดํ•ดํ•ด์•ผ ํ•ด์š”

React๋Š” Virtual DOM์„ ์‚ฌ์šฉํ•˜์—ฌ ํšจ์œจ์ ์œผ๋กœ UI๋ฅผ ์—…๋ฐ์ดํŠธํ•ด์š”.
์–ด๋–ค ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง๋˜๋ฉด, React๋Š” ์ƒˆ๋กœ์šด Virtual DOM ํŠธ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ด์ „ Virtual DOM ํŠธ๋ฆฌ์™€ ๋น„๊ต(Diffing)ํ•ด์„œ ์‹ค์ œ DOM์— ํ•„์š”ํ•œ ์ตœ์†Œํ•œ์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ๋งŒ ์ ์šฉ(Reconciliation)ํ•˜์ฃ .
์ด ๊ณผ์ • ์ž์ฒด๋Š” ๋งค์šฐ ํšจ์œจ์ ์ด์ง€๋งŒ, Diffing ๊ณผ์ • ์ž์ฒด๋„ ๋น„์šฉ์ด ๋“ค๊ณ , ๋ฌด์—‡๋ณด๋‹ค ์ปดํฌ๋„ŒํŠธ ํ•จ์ˆ˜ ์ž์ฒด๊ฐ€ ๋‹ค์‹œ ํ˜ธ์ถœ๋˜๋Š” ๊ฒƒ(๋ Œ๋”๋ง)์€ ํ”ผํ•  ์ˆ˜ ์—†์–ด์š”.
๋”ฐ๋ผ์„œ ๋ถˆํ•„์š”ํ•œ ์ปดํฌ๋„ŒํŠธ ํ•จ์ˆ˜์˜ ํ˜ธ์ถœ ํšŸ์ˆ˜๋ฅผ ์ค„์ด๋Š” ๊ฒƒ์ด ๋ Œ๋”๋ง ์ตœ์ ํ™”์˜ ํ•ต์‹ฌ ๋ชฉํ‘œ๊ฐ€ ๋œ๋‹ต๋‹ˆ๋‹ค.

โš™๏ธ ๋ Œ๋”๋ง ์ตœ์ ํ™”์˜ ํ•ต์‹ฌ ๋„๊ตฌ: useMemo, useCallback, React.memo

React์—์„œ ๋ถˆํ•„์š”ํ•œ ๋ Œ๋”๋ง์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ค๊ธฐ ์œ„ํ•œ ์„ธ ๊ฐ€์ง€ ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ๊ฐ€ ์žˆ์–ด์š”. ๋ฐ”๋กœ useMemo, useCallback, ๊ทธ๋ฆฌ๊ณ  React.memo์ž…๋‹ˆ๋‹ค.
์ด ์„ธ ๊ฐ€์ง€๋Š” ๋ชจ๋‘ "๋ฉ”๋ชจ์ด์ œ์ด์…˜(Memoization)"์ด๋ผ๋Š” ๊ฐœ๋…์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•ด์š”. ๋ฉ”๋ชจ์ด์ œ์ด์…˜์€ ์ด์ „์— ๊ณ„์‚ฐํ•œ ๊ฐ’์„ ์ €์žฅํ•ด๋‘์—ˆ๋‹ค๊ฐ€ ๋™์ผํ•œ ์ž…๋ ฅ์ด ๋“ค์–ด์˜ค๋ฉด ๋‹ค์‹œ ๊ณ„์‚ฐํ•˜์ง€ ์•Š๊ณ  ์ €์žฅ๋œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ตœ์ ํ™” ๊ธฐ๋ฒ•์„ ๋งํ•ด์š”.

0๏ธโƒฃ useMemo: ๊ฐ’(Value)์˜ ๋ฉ”๋ชจ์ด์ œ์ด์…˜ ๐Ÿ’ก

useMemo ํ›…์€ ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ ํŠน์ • "๊ฐ’"์˜ ์—ฐ์‚ฐ ๊ฒฐ๊ณผ๋ฅผ ๋ฉ”๋ชจ์ด์ œ์ด์…˜ํ•  ๋•Œ ์‚ฌ์šฉํ•ด์š”.
๋ณต์žกํ•œ ๊ณ„์‚ฐ์ด๋‚˜ ํฐ ๊ฐ์ฒด ์ƒ์„ฑ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ, ์˜์กด์„ฑ ๋ฐฐ์—ด์— ์žˆ๋Š” ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š” ํ•œ ์ด์ „์— ๊ณ„์‚ฐ๋œ ๊ฐ’์„ ์žฌ์‚ฌ์šฉํ•˜์—ฌ ๋ถˆํ•„์š”ํ•œ ์—ฐ์‚ฐ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์–ด์š”.

๐Ÿ“ useMemo ์‚ฌ์šฉ๋ฒ•

import React, { useState, useMemo } from 'react'; interface Item { id: number; name: string; price: number; quantity: number; } function ShoppingCart() { const [items, setItems] = useState<Item[]>([ { id: 1, name: 'Laptop', price: 1200, quantity: 1 }, { id: 2, name: 'Mouse', price: 25, quantity: 2 }, { id: 3, name: 'Keyboard', price: 75, quantity: 1 }, ]); const [shippingFee, setShippingFee] = useState(50); // useMemo๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด ๊ฐ€๊ฒฉ์„ ๋ฉ”๋ชจ์ด์ œ์ด์…˜ํ•ด์š”. // items ๋ฐฐ์—ด์ด๋‚˜ shippingFee๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งŒ ๋‹ค์‹œ ๊ณ„์‚ฐํ•ด์š”. const totalCost = useMemo(() => { console.log('์ด ๊ฐ€๊ฒฉ์„ ๋‹ค์‹œ ๊ณ„์‚ฐํ•ด์š”...'); const subtotal = items.reduce((acc, item) => acc + item.price * item.quantity, 0); return subtotal + shippingFee; }, [items, shippingFee]); // ์˜์กด์„ฑ ๋ฐฐ์—ด const addItem = () => { setItems(prevItems => [ ...prevItems, { id: prevItems.length + 1, name: `New Item ${prevItems.length + 1}`, price: 100, quantity: 1 }, ]); }; const changeShippingFee = () => { setShippingFee(prevFee => prevFee + 10); }; const handleTestRender = () => { console.log('๋ Œ๋”๋ง ํ…Œ์ŠคํŠธ ๋ฒ„ํŠผ ํด๋ฆญ๋จ'); // ์ด ๋ฒ„ํŠผ์€ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ, totalCost๋Š” ๋‹ค์‹œ ๊ณ„์‚ฐ๋˜์ง€ ์•Š์•„์š”. }; return ( <div> <h1>์žฅ๋ฐ”๊ตฌ๋‹ˆ</h1> <ul> {items.map(item => ( <li key={item.id}> {item.name} - ${item.price} x {item.quantity} </li> ))} </ul> <p>๋ฐฐ์†ก๋น„: ${shippingFee}</p> <p>์ด ๊ฒฐ์ œ ๊ธˆ์•ก: ${totalCost}</p> {/* totalCost๋Š” useMemo๋กœ ๋ฉ”๋ชจ์ด์ œ์ด์…˜๋œ ๊ฐ’์ด์—์š” */} <button onClick={addItem}>์•„์ดํ…œ ์ถ”๊ฐ€</button> <button onClick={changeShippingFee}>๋ฐฐ์†ก๋น„ ๋ณ€๊ฒฝ</button> <button onClick={handleTestRender}>๋ Œ๋”๋ง ํ…Œ์ŠคํŠธ</button> </div> ); } export default ShoppingCart;

์œ„ ์ฝ”๋“œ์—์„œ totalCost๋Š” items ๋ฐฐ์—ด์ด๋‚˜ shippingFee๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งŒ ๋‹ค์‹œ ๊ณ„์‚ฐ๋ผ์š”.
๋งŒ์•ฝ addItem ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ items๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด totalCost๋Š” ๋‹ค์‹œ ๊ณ„์‚ฐ๋˜์ง€๋งŒ, changeShippingFee ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ shippingFee๋งŒ ๋ณ€๊ฒฝ๋˜๊ฑฐ๋‚˜, ๋ Œ๋”๋ง ํ…Œ์ŠคํŠธ ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ์žฌ๋ Œ๋”๋ง๋˜๋”๋ผ๋„ items์™€ shippingFee๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์•˜๋‹ค๋ฉด totalCost๋Š” ๋‹ค์‹œ ๊ณ„์‚ฐ๋˜์ง€ ์•Š๊ณ  ์ด์ „์— ๋ฉ”๋ชจ์ด์ œ์ด์…˜๋œ ๊ฐ’์„ ์‚ฌ์šฉํ•ด์š”.
์ฝ˜์†” ๋กœ๊ทธ๋ฅผ ํ†ตํ•ด ์ด๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค.

๊ฒฝ๊ณ 

์ฃผ์˜์‚ฌํ•ญ: useMemo๋Š” ๋‚จ์šฉํ•˜๋ฉด ์˜คํžˆ๋ ค ์„ฑ๋Šฅ์„ ์ €ํ•˜์‹œํ‚ฌ ์ˆ˜ ์žˆ์–ด์š”.
๋ฉ”๋ชจ์ด์ œ์ด์…˜ ์ž์ฒด์—๋„ ๋น„์šฉ(๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ, ์˜์กด์„ฑ ๋น„๊ต)์ด ๋“ค๊ธฐ ๋•Œ๋ฌธ์—, ๊ณ„์‚ฐ ๋น„์šฉ์ด ์ ์€ ๊ฐ’์— useMemo๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์˜คํžˆ๋ ค ์†ํ•ด์ผ ์ˆ˜ ์žˆ์–ด์š”.
์ •๋ง๋กœ ๋ณต์žกํ•˜๊ณ  ๋น„์šฉ์ด ๋งŽ์ด ๋“œ๋Š” ๊ณ„์‚ฐ์—๋งŒ ์ ์šฉํ•˜๋Š” ๊ฒƒ์ด ํ˜„๋ช…ํ•ด์š”.

1๏ธโƒฃ useCallback: ํ•จ์ˆ˜(Function)์˜ ๋ฉ”๋ชจ์ด์ œ์ด์…˜ ๐Ÿš€

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

๐Ÿ“ useCallback ์‚ฌ์šฉ๋ฒ•

import React, { useState, useCallback, memo } from 'react'; // ์ž์‹ ์ปดํฌ๋„ŒํŠธ // React.memo๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ props๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์œผ๋ฉด ์žฌ๋ Œ๋”๋ง์„ ๋ฐฉ์ง€ํ•ด์š”. const ChildComponent = memo(({ onClick }: { onClick: () => void }) => { console.log('ChildComponent๊ฐ€ ๋ Œ๋”๋ง๋˜์—ˆ์–ด์š”.'); return ( <button onClick={onClick}> ์ž์‹ ๋ฒ„ํŠผ ํด๋ฆญ </button> ); }); function ParentComponent() { const [count, setCount] = useState(0); const [text, setText] = useState(''); // handleClick ํ•จ์ˆ˜๋ฅผ useCallback์œผ๋กœ ๋ฉ”๋ชจ์ด์ œ์ด์…˜ํ•ด์š”. // count๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งŒ ํ•จ์ˆ˜๊ฐ€ ์ƒˆ๋กœ ์ƒ์„ฑ๋ผ์š”. const handleClick = useCallback(() => { console.log(`๋ฒ„ํŠผ์ด ํด๋ฆญ๋˜์—ˆ์–ด์š”. ํ˜„์žฌ ์นด์šดํŠธ: ${count}`); setCount(prevCount => prevCount + 1); }, [count]); // ์˜์กด์„ฑ ๋ฐฐ์—ด์— count๋ฅผ ๋„ฃ์–ด์ฃผ์„ธ์š”. const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => { setText(e.target.value); }, []); // ์˜์กด์„ฑ ๋ฐฐ์—ด์ด ๋น„์–ด์žˆ์œผ๋ฉด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งˆ์šดํŠธ๋  ๋•Œ ํ•œ ๋ฒˆ๋งŒ ์ƒ์„ฑ๋ผ์š”. return ( <div> <h1>๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ</h1> <p>์นด์šดํŠธ: {count}</p> <input type="text" value={text} onChange={handleChange} placeholder="ํ…์ŠคํŠธ ์ž…๋ ฅ" /> <br /><br /> <ChildComponent onClick={handleClick} /> {/* ๋ฉ”๋ชจ์ด์ œ์ด์…˜๋œ ํ•จ์ˆ˜๋ฅผ ์ „๋‹ฌํ•ด์š” */} <button onClick={() => setCount(prevCount => prevCount + 1)}>์นด์šดํŠธ ์ฆ๊ฐ€</button> <button onClick={() => setText('์ƒˆ๋กœ์šด ํ…์ŠคํŠธ')}>ํ…์ŠคํŠธ ๋ณ€๊ฒฝ</button> </div> ); } export default ParentComponent;

ParentComponent๊ฐ€ ๋ Œ๋”๋ง๋  ๋•Œ๋งˆ๋‹ค handleClick ํ•จ์ˆ˜๋Š” ๋งค๋ฒˆ ์ƒˆ๋กœ ์ƒ์„ฑ๋ผ์š”.
ํ•˜์ง€๋งŒ useCallback์„ ์‚ฌ์šฉํ•˜๋ฉด count ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งŒ handleClick ํ•จ์ˆ˜๊ฐ€ ์ƒˆ๋กœ ์ƒ์„ฑ๋˜๊ณ , ๊ทธ ์™ธ์˜ ๊ฒฝ์šฐ์—๋Š” ์ด์ „์— ์ƒ์„ฑ๋œ ํ•จ์ˆ˜๋ฅผ ์žฌ์‚ฌ์šฉํ•ด์š”.
ChildComponent๋Š” React.memo๋กœ ๊ฐ์‹ธ์ ธ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, onClick prop์œผ๋กœ ์ „๋‹ฌ๋˜๋Š” handleClick ํ•จ์ˆ˜๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š” ํ•œ ์žฌ๋ Œ๋”๋ง๋˜์ง€ ์•Š๋Š”๋‹ต๋‹ˆ๋‹ค.
๋งŒ์•ฝ useCallback์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด, ParentComponent๊ฐ€ ๋ Œ๋”๋ง๋  ๋•Œ๋งˆ๋‹ค handleClick ํ•จ์ˆ˜๊ฐ€ ์ƒˆ๋กœ ์ƒ์„ฑ๋˜๊ณ , ์ด๋Š” ChildComponent์˜ onClick prop์ด ๋ณ€๊ฒฝ๋œ ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผ๋˜์–ด ChildComponent๋„ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ์žฌ๋ Œ๋”๋ง๋  ๊ฑฐ์˜ˆ์š”.
text ์ž…๋ ฅ ํ•„๋“œ์˜ ๊ฐ’์„ ๋ณ€๊ฒฝํ•ด๋„ handleClick์€ ์ƒˆ๋กœ ์ƒ์„ฑ๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ ChildComponent๋Š” ์žฌ๋ Œ๋”๋ง๋˜์ง€ ์•Š์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์š”.

์œ ์šฉํ•œ ํŒ

์–ธ์ œ useCallback์„ ์‚ฌ์šฉํ•ด์•ผ ํ• ๊นŒ์š”?
์ฃผ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒฝ์šฐ์— useCallback์„ ๊ณ ๋ คํ•ด ๋ณผ ์ˆ˜ ์žˆ์–ด์š”.

  1. React.memo๋กœ ๊ฐ์‹ธ์ง„ ์ž์‹ ์ปดํฌ๋„ŒํŠธ์— props๋กœ ํ•จ์ˆ˜๋ฅผ ์ „๋‹ฌํ•  ๋•Œ
  2. useEffect์˜ ์˜์กด์„ฑ ๋ฐฐ์—ด์— ํ•จ์ˆ˜๋ฅผ ๋„ฃ์–ด์•ผ ํ•  ๋•Œ (๋ฌดํ•œ ๋ฃจํ”„ ๋ฐฉ์ง€)
  3. useContext๋ฅผ ํ†ตํ•ด ํ•จ์ˆ˜๋ฅผ ์ „๋‹ฌํ•  ๋•Œ

2๏ธโƒฃ React.memo: ์ปดํฌ๋„ŒํŠธ(Component)์˜ ๋ฉ”๋ชจ์ด์ œ์ด์…˜ โ™ป๏ธ

React.memo๋Š” ๊ณ ์ฐจ ์ปดํฌ๋„ŒํŠธ(Higher-Order Component, HOC)๋กœ, ๋ž˜ํ•‘๋œ ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ์˜ props๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์•˜๋‹ค๋ฉด ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๋ฅผ ์žฌ๋ Œ๋”๋งํ•˜์ง€ ์•Š๋„๋ก ํ•ด์ค˜์š”.
์ด๋Š” shouldComponentUpdate ๋ผ์ดํ”„์‚ฌ์ดํด ๋ฉ”์„œ๋“œ๋ฅผ ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๊ณผ ์œ ์‚ฌํ•ด์š”.

๐Ÿ“ React.memo ์‚ฌ์šฉ๋ฒ•

์œ„ useCallback ์˜ˆ์‹œ์—์„œ ChildComponent๋ฅผ React.memo๋กœ ๊ฐ์ŒŒ๋˜ ๊ฒƒ์„ ๊ธฐ์–ตํ•˜์‹œ์ฃ ?
React.memo์˜ ๊ธฐ๋ณธ ๋™์ž‘์€ props์˜ "์–•์€ ๋น„๊ต(shallow comparison)"๋ฅผ ์ˆ˜ํ–‰ํ•ด์š”.
์ฆ‰, ๊ฐ์ฒด๋‚˜ ๋ฐฐ์—ด ๊ฐ™์€ ์ฐธ์กฐ ํƒ€์ž…์˜ prop์€ ๋‚ด๋ถ€ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜์—ˆ๋”๋ผ๋„ ์ฐธ์กฐ ์ž์ฒด๊ฐ€ ๋™์ผํ•˜๋ฉด ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์€ ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผํ•œ๋‹ต๋‹ˆ๋‹ค.
๋งŒ์•ฝ ๊นŠ์€ ๋น„๊ต๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋ฉด, React.memo์˜ ๋‘ ๋ฒˆ์งธ ์ธ์ž๋กœ ๋น„๊ต ํ•จ์ˆ˜๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์–ด์š”.

import React, { memo } from 'react'; interface User { id: number; name: string; email: string; } interface UserProfileProps { user: User; onEdit: () => void; } // React.memo๋กœ UserProfile ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ์‹ธ์š”. // user ๊ฐ์ฒด๋‚˜ onEdit ํ•จ์ˆ˜๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์œผ๋ฉด ์žฌ๋ Œ๋”๋ง๋˜์ง€ ์•Š์•„์š”. const UserProfile = memo(({ user, onEdit }: UserProfileProps) => { console.log(`UserProfile ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง: ${user.name}`); return ( <div> <h2>{user.name}</h2> <p>์ด๋ฉ”์ผ: {user.email}</p> <button onClick={onEdit}>์ˆ˜์ •</button> </div> ); }); // ์ปค์Šคํ…€ ๋น„๊ต ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์˜ˆ์‹œ (์„ ํƒ ์‚ฌํ•ญ) interface ComplexData { value: number; config: { theme: string; level: number; }; } interface ComplexComponentProps { data: ComplexData; } // data prop์˜ config ๊ฐ์ฒด ๋‚ด๋ถ€๊นŒ์ง€ ๋น„๊ตํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด custom comparator๋ฅผ ์‚ฌ์šฉํ•ด์š”. const ComplexComponent = memo(({ data }: ComplexComponentProps) => { console.log('ComplexComponent ๋ Œ๋”๋ง'); return ( <div> <p>Value: {data.value}</p> <p>Theme: {data.config.theme}</p> </div> ); }, (prevProps, nextProps) => { // ์ด์ „ props์™€ ๋‹ค์Œ props๋ฅผ ๋น„๊ตํ•˜์—ฌ true๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋ Œ๋”๋ง ์•ˆ ํ•จ, false๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋ Œ๋”๋ง ํ•จ return ( prevProps.data.value === nextProps.data.value && prevProps.data.config.theme === nextProps.data.config.theme && prevProps.data.config.level === nextProps.data.config.level ); }); export default function App() { const [currentUser, setCurrentUser] = React.useState<User>({ id: 1, name: '๋ธ”๋ฃจ', email: 'blue@example.com' }); const [otherState, setOtherState] = React.useState(0); // onEdit ํ•จ์ˆ˜๋Š” useCallback์œผ๋กœ ๋ฉ”๋ชจ์ด์ œ์ด์…˜ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์•„์š”. const handleEdit = React.useCallback(() => { console.log(`${currentUser.name}๋‹˜ ์ •๋ณด ์ˆ˜์ •`); // ์ˆ˜์ • ๋กœ์ง... }, [currentUser]); // currentUser๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งŒ ํ•จ์ˆ˜๊ฐ€ ์ƒˆ๋กœ ์ƒ์„ฑ๋ผ์š”. const [complexData, setComplexData] = React.useState<ComplexData>({ value: 10, config: { theme: 'dark', level: 1 } }); return ( <div> <h1>๋ฉ”์ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜</h1> <UserProfile user={currentUser} onEdit={handleEdit} /> {/* currentUser๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์œผ๋ฉด UserProfile์€ ์žฌ๋ Œ๋”๋ง๋˜์ง€ ์•Š์•„์š” */} <button onClick={() => setOtherState(prev => prev + 1)}>๋‹ค๋ฅธ ์ƒํƒœ ๋ณ€๊ฒฝ ({otherState})</button> <button onClick={() => setCurrentUser(prev => ({ ...prev, name: '์ƒˆ๋กœ์šด ๋ธ”๋ฃจ' }))}>์‚ฌ์šฉ์ž ์ด๋ฆ„ ๋ณ€๊ฒฝ</button> <hr /> <ComplexComponent data={complexData} /> <button onClick={() => setComplexData(prev => ({ ...prev, value: prev.value + 1 }))}>๋ณต์žก ๋ฐ์ดํ„ฐ ๊ฐ’ ๋ณ€๊ฒฝ</button> <button onClick={() => setComplexData(prev => ({ ...prev, config: { ...prev.config, level: prev.config.level + 1 } }))}>๋ณต์žก ๋ฐ์ดํ„ฐ ๋ ˆ๋ฒจ ๋ณ€๊ฒฝ</button> </div> ); }

UserProfile ์ปดํฌ๋„ŒํŠธ๋Š” React.memo๋กœ ๊ฐ์‹ธ์ ธ ์žˆ์–ด์š”.
otherState๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ๋„ UserProfile์˜ user prop๊ณผ onEdit prop์ด ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ, UserProfile์€ ์žฌ๋ Œ๋”๋ง๋˜์ง€ ์•Š๋Š” ๊ฒƒ์„ ์ฝ˜์†” ๋กœ๊ทธ๋ฅผ ํ†ตํ•ด ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒ ์‚ฌ์šฉ์ž ์ด๋ฆ„ ๋ณ€๊ฒฝ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด currentUser ๊ฐ์ฒด ์ž์ฒด๊ฐ€ ์ƒˆ๋กœ ์ƒ์„ฑ๋˜์–ด user prop์ด ๋ณ€๊ฒฝ๋œ ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผ๋˜๊ณ , UserProfile์€ ์žฌ๋ Œ๋”๋ง๋ผ์š”.
ComplexComponent์˜ ๊ฒฝ์šฐ, data prop์˜ config.level๋งŒ ๋ณ€๊ฒฝํ•ด๋„ ์žฌ๋ Œ๋”๋ง์ด ์ผ์–ด๋‚˜์ง€ ์•Š๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์–ด์š”. ์ด๋Š” ์ปค์Šคํ…€ ๋น„๊ต ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด level ๊ฐ’๊นŒ์ง€ ๋น„๊ตํ•˜๊ธฐ ๋•Œ๋ฌธ์ด์—์š”.

๊ฒฝ๊ณ 

์ฃผ์˜์‚ฌํ•ญ: React.memo๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ, props๋กœ ํ•จ์ˆ˜๋ฅผ ์ „๋‹ฌํ•œ๋‹ค๋ฉด ๋ฐ˜๋“œ์‹œ useCallback์œผ๋กœ ํ•ด๋‹น ํ•จ์ˆ˜๋ฅผ ๋ฉ”๋ชจ์ด์ œ์ด์…˜ํ•ด์•ผ ํ•ด์š”.
๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ Œ๋”๋ง๋  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ํ•จ์ˆ˜๊ฐ€ ์ƒ์„ฑ๋˜๊ณ , React.memo๋Š” prop์ด ๋ณ€๊ฒฝ๋˜์—ˆ๋‹ค๊ณ  ํŒ๋‹จํ•˜์—ฌ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ์žฌ๋ Œ๋”๋งํ•˜๊ฒŒ ๋œ๋‹ต๋‹ˆ๋‹ค.
๋˜ํ•œ, React.memo๋„ ์–•์€ ๋น„๊ต ๋น„์šฉ์ด ๋ฐœ์ƒํ•˜๋ฏ€๋กœ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ์— ๋ฌด๋ถ„๋ณ„ํ•˜๊ฒŒ ์ ์šฉํ•˜๊ธฐ๋ณด๋‹ค๋Š” ๋ Œ๋”๋ง ๋น„์šฉ์ด ํฐ ์ปดํฌ๋„ŒํŠธ์— ์„ ๋ณ„์ ์œผ๋กœ ์ ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์•„์š”.

๐Ÿงช ์‹ค์ „ ๋ Œ๋”๋ง ์ตœ์ ํ™” ์ „๋žต๊ณผ ํ”ํ•œ ์‹ค์ˆ˜ ๐Ÿคฆโ€โ™€๏ธ

0๏ธโƒฃ ์–ธ์ œ, ์–ด๋–ป๊ฒŒ ์ ์šฉํ•ด์•ผ ํ• ๊นŒ์š”?

  • ์ปดํฌ๋„ŒํŠธ ํ”„๋กœํŒŒ์ผ๋ง: React DevTools์˜ Profiler ํƒญ์„ ์‚ฌ์šฉํ•˜์—ฌ ์–ด๋–ค ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ์ž์ฃผ ๋ Œ๋”๋ง๋˜๋Š”์ง€, ๋ Œ๋”๋ง ๋น„์šฉ์ด ๋†’์€์ง€ ํŒŒ์•…ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ด์š”.
    ๋ณ‘๋ชฉ ์ง€์ ์„ ์ •ํ™•ํžˆ ์ฐพ์•„๋‚ธ ํ›„ ์ตœ์ ํ™”๋ฅผ ์‹œ์ž‘ํ•ด์•ผ ๊ฐ€์žฅ ํฐ ํšจ๊ณผ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค.
  • ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ์— ๋Œ€ํ•œ Props ์•ˆ์ •ํ™”: ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์˜ ๋ Œ๋”๋ง์œผ๋กœ ์ธํ•ด ์ž์‹ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ๋ Œ๋”๋ง๋˜๋Š” ๊ฒฝ์šฐ, ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋ฅผ React.memo๋กœ ๊ฐ์‹ธ๊ณ , ์ž์‹์—๊ฒŒ ์ „๋‹ฌ๋˜๋Š” props (ํŠนํžˆ ๊ฐ์ฒด, ๋ฐฐ์—ด, ํ•จ์ˆ˜)๋ฅผ useMemo๋‚˜ useCallback์œผ๋กœ ๋ฉ”๋ชจ์ด์ œ์ด์…˜ํ•˜์—ฌ ์•ˆ์ •ํ™”ํ•ด์•ผ ํ•ด์š”.
  • ๋ณต์žกํ•œ ๊ณ„์‚ฐ ๊ฒฐ๊ณผ ๋ฉ”๋ชจ์ด์ œ์ด์…˜: ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ ๋ฐฐ์—ด ํ•„ํ„ฐ๋ง, ์ •๋ ฌ, ํฐ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ๋ณ€ํ™˜ ๋“ฑ ๋น„์šฉ์ด ๋งŽ์ด ๋“œ๋Š” ๊ณ„์‚ฐ์ด ์žˆ๋‹ค๋ฉด useMemo๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฒฐ๊ณผ๋ฅผ ์บ์‹ฑํ•˜์„ธ์š”.

1๏ธโƒฃ ํ”ํžˆ ์ €์ง€๋ฅด๋Š” ์‹ค์ˆ˜๋“ค

  • ๋ถˆํ•„์š”ํ•œ ๋ฉ”๋ชจ์ด์ œ์ด์…˜: ๋ชจ๋“  ๊ฐ’์ด๋‚˜ ํ•จ์ˆ˜๋ฅผ ๋ฉ”๋ชจ์ด์ œ์ด์…˜ํ•˜๋Š” ๊ฒƒ์€ ์˜คํžˆ๋ ค ์˜ค๋ฒ„ํ—ค๋“œ๋งŒ ์ฆ๊ฐ€์‹œ์ผœ์š”.
    ๋ฉ”๋ชจ์ด์ œ์ด์…˜ ์ž์ฒด์—๋„ ๋น„์šฉ์ด ๋“ค๊ธฐ ๋•Œ๋ฌธ์—, ์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ๋ช…ํ™•ํ•œ ๊ณณ์—๋งŒ ์„ ๋ณ„์ ์œผ๋กœ ์ ์šฉํ•ด์•ผ ํ•ด์š”.
  • ์ž˜๋ชป๋œ ์˜์กด์„ฑ ๋ฐฐ์—ด: useMemo๋‚˜ useCallback์˜ ์˜์กด์„ฑ ๋ฐฐ์—ด์„ ๋น„์›Œ๋‘๊ฑฐ๋‚˜, ํ•„์š”ํ•œ ์˜์กด์„ฑ์„ ๋ˆ„๋ฝํ•˜๋ฉด ์˜ค๋ž˜๋œ ํด๋กœ์ € ๊ฐ’์„ ์ฐธ์กฐํ•˜๊ฑฐ๋‚˜ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋ฒ„๊ทธ๋ฅผ ์œ ๋ฐœํ•  ์ˆ˜ ์žˆ์–ด์š”.
    ๋ฐ˜๋Œ€๋กœ ๋„ˆ๋ฌด ๋งŽ์€ ์˜์กด์„ฑ์„ ๋„ฃ์œผ๋ฉด ๋ฉ”๋ชจ์ด์ œ์ด์…˜ ํšจ๊ณผ๊ฐ€ ์‚ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์œผ๋‹ˆ ์ฃผ์˜ํ•ด์•ผ ํ•ด์š”.
  • ๊ฐ์ฒด/๋ฐฐ์—ด prop์˜ ๋ถˆ๋ณ€์„ฑ ์œ ์ง€ ์‹คํŒจ: React.memo๋ฅผ ์‚ฌ์šฉํ•˜๋”๋ผ๋„, props๋กœ ์ „๋‹ฌ๋˜๋Š” ๊ฐ์ฒด๋‚˜ ๋ฐฐ์—ด์ด ๋งค๋ฒˆ ์ƒˆ๋กœ์šด ์ฐธ์กฐ๋กœ ์ƒ์„ฑ๋œ๋‹ค๋ฉด React.memo๋Š” ๋ฌด์šฉ์ง€๋ฌผ์ด ๋ผ์š”.
    ํ•ญ์ƒ ๋ถˆ๋ณ€์„ฑ(immutability)์„ ์œ ์ง€ํ•˜๋ฉฐ ์ƒˆ๋กœ์šด ๊ฐ์ฒด๋‚˜ ๋ฐฐ์—ด์„ ์ƒ์„ฑํ•˜๋Š” ์Šต๊ด€์„ ๋“ค์—ฌ์•ผ ํ•œ๋‹ต๋‹ˆ๋‹ค.
    // ์ž˜๋ชป๋œ ์˜ˆ์‹œ: ๋งค๋ฒˆ ์ƒˆ๋กœ์šด ๊ฐ์ฒด ์ƒ์„ฑ <ChildComponent user={{ name: '๋ธ”๋ฃจ', age: 30 }} /> // ์˜ฌ๋ฐ”๋ฅธ ์˜ˆ์‹œ: useMemo๋กœ ๊ฐ์ฒด ๋ฉ”๋ชจ์ด์ œ์ด์…˜ const user = useMemo(() => ({ name: '๋ธ”๋ฃจ', age: 30 }), []); <ChildComponent user={user} />

๐Ÿ“ ์ •๋ฆฌํ•˜๋ฉฐ: ํ˜„๋ช…ํ•œ ์ตœ์ ํ™”๊ฐ€ ์ค‘์š”ํ•ด์š”

์˜ค๋Š˜์€ React ๋ Œ๋”๋ง ์ตœ์ ํ™”์˜ ํ•ต์‹ฌ ๋„๊ตฌ์ธ useMemo, useCallback, React.memo์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์•Œ์•„๋ณด์•˜์–ด์š”.
์ด ์„ธ ๊ฐ€์ง€ ํ›…๊ณผ HOC๋Š” React ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์„ฑ๋Šฅ์„ ํฌ๊ฒŒ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ๋“ค์ด์—์š”.
ํ•˜์ง€๋งŒ ๋ฌด๋ถ„๋ณ„ํ•œ ์‚ฌ์šฉ์€ ์˜คํžˆ๋ ค ์ฝ”๋“œ๋ฅผ ๋ณต์žกํ•˜๊ฒŒ ๋งŒ๋“ค๊ณ  ๋””๋ฒ„๊น…์„ ์–ด๋ ต๊ฒŒ ํ•˜๋ฉฐ, ๋•Œ๋กœ๋Š” ์„ฑ๋Šฅ์„ ์ €ํ•˜์‹œํ‚ฌ ์ˆ˜๋„ ์žˆ๋‹ค๋Š” ์ ์„ ํ•ญ์ƒ ๊ธฐ์–ตํ•ด์•ผ ํ•ด์š”.
์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ์˜์‹ฌ๋˜๋Š” ์ง€์ ์„ ์ •ํ™•ํžˆ ํŒŒ์•…ํ•˜๊ณ , ํ•„์š”ํ•œ ๊ณณ์—๋งŒ ํ˜„๋ช…ํ•˜๊ฒŒ ์ ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ง„์ •ํ•œ ์ตœ์ ํ™”์˜ ๊ธธ์ด๋ผ๊ณ  ๋ง์”€๋“œ๋ฆฌ๊ณ  ์‹ถ์–ด์š”.
์—ฌ๋Ÿฌ๋ถ„์˜ React ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋”์šฑ ๋น ๋ฅด๊ณ  ๋งค๋„๋Ÿฝ๊ฒŒ ๋™์ž‘ํ•˜๊ธฐ๋ฅผ ๋ฐ”๋ผ๋ฉฐ, ์ €๋Š” ๋‹ค์Œ์—๋„ ์œ ์ตํ•œ ์ •๋ณด๋กœ ์ฐพ์•„์˜ฌ๊ฒŒ์š”!

๐Ÿ“ฎ ์ฐธ๊ณ 

์—ฐ๊ด€๋œ ํฌ์ŠคํŠธ