[React] 7์ผ์ฐจ

๊น€๋ฏผ์ง€ยท2026๋…„ 4์›” 30์ผ

kosa-Spring

๋ชฉ๋ก ๋ณด๊ธฐ
13/13

React ํ•™์Šต ์ •๋ฆฌ ๋…ธํŠธ

๐Ÿ“… ์ •๋ฆฌ์ผ: 2026-04-29
๐Ÿ“š ๋ฒ”์œ„: Props & State ์—ฐ๋™ / Component LifeCycle / Routing / ์„ฑ๋Šฅ ์ตœ์ ํ™” (useMemo, useCallback, React.memo)


Ch03. Props & State ์—ฐ๋™

ํ•ต์‹ฌ ์›์น™

  • ๋ถ€๋ชจ state โ†’ ์ž์‹ UI ๋ณ€๊ฒฝ: ๋ถ€๋ชจ์˜ state๋ฅผ props๋กœ ์ž์‹์—๊ฒŒ ์ „๋‹ฌ
  • ์ž์‹์ด ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๊ณ  ์‹ถ์„ ๋•Œ: ์ž์‹์€ props๋ฅผ ์ง์ ‘ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋‹ค
    • ๋ถ€๋ชจ๊ฐ€ ์ƒํƒœ ๋ณ€๊ฒฝ ํ•จ์ˆ˜(setState)๋ฅผ props๋กœ ํ•จ๊ป˜ ์ „๋‹ฌ
    • ์ž์‹์ด ๊ทธ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœ โ†’ ๋ถ€๋ชจ state ๋ณ€๊ฒฝ โ†’ ๋ถ€๋ชจ๊ฐ€ ์ƒˆ props๋ฅผ ์ž์‹์—๊ฒŒ ๋‹ค์‹œ ๋‚ด๋ ค์คŒ

๋ฐ์ดํ„ฐ ํ๋ฆ„ ์š”์•ฝ

๋ถ€๋ชจ (state ๋ณด์œ )
  โ†“ props๋กœ state ์ „๋‹ฌ
์ž์‹ (UI ๋ Œ๋”๋ง)
  โ†“ ๋ณ€๊ฒฝ์ด ํ•„์š”ํ•˜๋ฉด ๋ถ€๋ชจ๊ฐ€ ์ค€ setState ํ•จ์ˆ˜ ํ˜ธ์ถœ
๋ถ€๋ชจ (state ์—…๋ฐ์ดํŠธ)
  โ†“ ์ƒˆ props ๋‹ค์‹œ ์ „๋‹ฌ
์ž์‹ (๋ฆฌ๋ Œ๋”๋ง)

Ch04. Component LifeCycle

์ปดํฌ๋„ŒํŠธ์˜ ์„ธ ๊ฐ€์ง€ ์ƒํƒœ

์ƒํƒœ์„ค๋ช…๋ฐœ์ƒ ์‹œ์ 
๋งˆ์šดํŠธ์ปดํฌ๋„ŒํŠธ DOM์ด ์ตœ์ดˆ ์ƒ์„ฑํŽ˜์ด์ง€์— ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ถ”๊ฐ€๋  ๋•Œ
์—…๋ฐ์ดํŠธDOM์ด ์ˆ˜์ •๋˜๋Š” ์ƒํƒœstate/props ๋ณ€๊ฒฝ, ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ ๋ฆฌ๋ Œ๋”๋ง ์‹œ
์–ธ๋งˆ์šดํŠธDOM์ด ์ œ๊ฑฐ๋˜๋Š” ์ƒํƒœํŽ˜์ด์ง€์—์„œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ œ๊ฑฐ๋  ๋•Œ

๐Ÿ’ก ์ถ”๊ฐ€ ์ •๋ฆฌ: React์—์„œ ๋ผ์ดํ”„์‚ฌ์ดํด์„ ๋‹ค๋ฃจ๋Š” ํ›…์€ useEffect๋‹ค.

  • useEffect(() => {}, []) โ†’ ๋งˆ์šดํŠธ ์‹œ 1ํšŒ ์‹คํ–‰
  • useEffect(() => {}, [state]) โ†’ state ๋ณ€๊ฒฝ ์‹œ๋งˆ๋‹ค ์‹คํ–‰ (์—…๋ฐ์ดํŠธ)
  • useEffect(() => { return () => {} }, []) โ†’ return ์•ˆ์˜ ํ•จ์ˆ˜๊ฐ€ ์–ธ๋งˆ์šดํŠธ ์‹œ ์‹คํ–‰

Ch05. Routing

ํŽ˜์ด์ง€ ์ด๋™ ๋ฐฉ๋ฒ•

๋ฐฉ๋ฒ•์‚ฌ์šฉ ์œ„์น˜์ฝ”๋“œ
<Link> ์ปดํฌ๋„ŒํŠธJSX ๋‚ด ์ด๋™<Link href="/page">์ด๋™</Link>
useRouter()ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธconst router = useRouter(); router.push('/page')
redirect()ํด๋ผ์ด์–ธํŠธ & ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ ๋ชจ๋‘ ๊ฐ€๋Šฅredirect('/page')

โš ๏ธ ์ฃผ์˜: ํŽ˜์ด์ง€ ์ปดํฌ๋„ŒํŠธ๋Š” ๋ฐ˜๋“œ์‹œ ํด๋” ํ˜•ํƒœ๋กœ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค. ์‹ฑ๊ธ€ ํŒŒ์ผ ๋ถˆ๊ฐ€.


Ch05. ์„ฑ๋Šฅ ์ตœ์ ํ™”

๋ฌธ์ œ ์ธ์‹: ์™œ ์„ฑ๋Šฅ ์ตœ์ ํ™”๊ฐ€ ํ•„์š”ํ•œ๊ฐ€?

React๋Š” state๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฆฌ๋ Œ๋”๋งํ•œ๋‹ค.
๋ฆฌ๋ Œ๋”๋ง๋  ๋•Œ๋งˆ๋‹ค ์ปดํฌ๋„ŒํŠธ ํ•จ์ˆ˜ ๋‚ด๋ถ€์˜ ๋ชจ๋“  ์ฝ”๋“œ๊ฐ€ ๋‹ค์‹œ ์‹คํ–‰๋˜๊ณ , ํ•จ์ˆ˜๋„ ์ƒˆ๋กœ ์„ ์–ธ๋œ๋‹ค.

  • ํ•จ์ˆ˜๊ฐ€ ์žฌ์„ ์–ธ๋˜๋ฉด โ†’ ์ด์ „ ํ•จ์ˆ˜ ๊ฐ์ฒด๋Š” ๋ฒ„๋ ค์ง€๊ณ  ์ƒˆ ๊ฐ์ฒด๊ฐ€ ์ƒ๊น€ โ†’ ์ฐธ์กฐ(๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ)๊ฐ€ ๋‹ฌ๋ผ์ง„๋‹ค
  • ํ•จ์ˆ˜๋ฅผ props๋กœ ์ž์‹์—๊ฒŒ ๋„˜๊ธธ ๊ฒฝ์šฐ โ†’ ์ฐธ์กฐ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ์ž์‹๋„ ๋ฆฌ๋ Œ๋”๋ง๋œ๋‹ค (๋ถˆํ•„์š”ํ•œ ๋ Œ๋”๋ง ๋ฐœ์ƒ)

useMemo โ€” ๊ณ„์‚ฐ ๊ฒฐ๊ณผ๊ฐ’์„ ๊ธฐ์–ต

์–ธ์ œ ์“ฐ๋‚˜?

  • ๋ Œ๋”๋ง๋งˆ๋‹ค ๋ฌด๊ฑฐ์šด ๊ณ„์‚ฐ์„ ๋ฐ˜๋ณตํ•˜๋Š” ๊ฒŒ ๋‚ญ๋น„์ผ ๋•Œ
  • ํŠน์ • ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งŒ ์žฌ๊ณ„์‚ฐํ•˜๊ณ , ๋‚˜๋จธ์ง€ ๊ฒฝ์šฐ์—” ์ด์ „ ๊ฐ’์„ ์žฌ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์„ ๋•Œ

๋ฌธ๋ฒ•

const ๊ฒฐ๊ณผ๊ฐ’ = useMemo(() => {
  // ๊ณ„์‚ฐ ๋กœ์ง
  return ๊ณ„์‚ฐ๋œ๊ฐ’;
}, [์˜์กด์„ฑ_์ƒํƒœ]);
  • ์˜์กด์„ฑ ๋ฐฐ์—ด์˜ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งŒ ์žฌ๊ณ„์‚ฐ
  • ๋‹ค๋ฅธ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋˜์–ด ๋ฆฌ๋ Œ๋”๋ง์ด ์ผ์–ด๋‚˜๋„ ์ด์ „์— ๋ฉ”๋ชจํ•œ ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉ

์‹ค์Šต ์˜ˆ์‹œ ๋น„๊ต

// โŒ ๋ฆฌ๋ Œ๋”๋ง๋  ๋•Œ๋งˆ๋‹ค ํ•จ์ˆ˜ ์žฌํ˜ธ์ถœ
const getLength1 = function () {
  return boards.length;
};

// โœ… boards๊ฐ€ ๋ฐ”๋€” ๋•Œ๋งŒ ์žฌ๊ณ„์‚ฐ
const getLength2 = useMemo(() => {
  return boards.length;
}, [boards]);

โš ๏ธ useMemo๋Š” ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ํ˜ธ์ถœํ•  ๋•Œ getLength2()๊ฐ€ ์•„๋‹ˆ๋ผ {getLength2}๋กœ ์‚ฌ์šฉ.


useCallback โ€” ํ•จ์ˆ˜ ์ž์ฒด๋ฅผ ๊ธฐ์–ต

์–ธ์ œ ์“ฐ๋‚˜?

  • ํ•จ์ˆ˜๋ฅผ ์ž์‹ ์ปดํฌ๋„ŒํŠธ์— props๋กœ ์ „๋‹ฌํ•  ๋•Œ
  • ๋ฆฌ๋ Œ๋”๋ง๋งˆ๋‹ค ํ•จ์ˆ˜๊ฐ€ ์ƒˆ๋กœ ์„ ์–ธ๋˜๋ฉด ์ž์‹๋„ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ๋ฆฌ๋ Œ๋”๋ง๋˜๋Š” ๊ฑธ ๋ง‰๊ณ  ์‹ถ์„ ๋•Œ

๋ฌธ๋ฒ•

const ํ•จ์ˆ˜ = useCallback(function () {
  // ํ•จ์ˆ˜ ๋กœ์ง
}, [์˜์กด์„ฑ_์ƒํƒœ]);
  • ์˜์กด์„ฑ ๋ฐฐ์—ด์˜ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งŒ ํ•จ์ˆ˜๋ฅผ ์ƒˆ๋กœ ์„ ์–ธ
  • ๋ฐฐ์—ด์ด []์ด๋ฉด ๋งˆ์šดํŠธ ์‹œ ๋”ฑ 1๋ฒˆ๋งŒ ์„ ์–ธ

useMemo vs useCallback ์ฐจ์ด

useMemouseCallback
๊ธฐ์–ตํ•˜๋Š” ๊ฒƒ๊ณ„์‚ฐ ๊ฒฐ๊ณผ๊ฐ’ํ•จ์ˆ˜ ์ž์ฒด
๋ฐ˜ํ™˜๊ฐ’ํ•จ์ˆ˜
์‚ฌ์šฉ ์‹œ{getLength2}{getLength1()} (ํ•จ์ˆ˜์ด๋ฏ€๋กœ ํ˜ธ์ถœ ๊ฐ€๋Šฅ)
์ฃผ์š” ์šฉ๋„๋ฌด๊ฑฐ์šด ๊ณ„์‚ฐ ์บ์‹ฑ์ž์‹์—๊ฒŒ ๋„˜๊ธธ ํ•จ์ˆ˜ ์•ˆ์ •ํ™”

์‹ค์Šต ์˜ˆ์‹œ

// โœ… boards๊ฐ€ ๋ฐ”๋€” ๋•Œ๋งŒ ํ•จ์ˆ˜ ์žฌ์ •์˜
const getLength1 = useCallback(function () {
  return boards.length;
}, [boards]);

// โœ… ๋งˆ์šดํŠธ ์‹œ 1๋ฒˆ๋งŒ ์ •์˜ (setBtitle์ด ์•ˆ์ •์ ์ด๋ผ deps ๋ถˆํ•„์š”)
const handleBtitleChange = useCallback(function (event) {
  setBtitle(event.target.value);
}, []);

// โœ… ์ƒํƒœ ์—…๋ฐ์ดํ„ฐ ํ•จ์ˆ˜ ํ˜•ํƒœ๋กœ ์“ฐ๋ฉด boards๋ฅผ deps์— ์•ˆ ๋„ฃ์–ด๋„ ๋จ
const removeBoard = useCallback((bno) => {
  setBoards(prev => prev.filter(board => board.bno !== bno));
}, []);

const changeBoard = useCallback((bno) => {
  setBoards(prev => prev.map(board =>
    board.bno === bno
      ? { ...board, btitle: board.btitle + " (๋ณ€๊ฒฝ)" }
      : board
  ));
}, []);

๐Ÿ’ก ์ƒํƒœ ์—…๋ฐ์ดํ„ฐ ํ•จ์ˆ˜ ํŒจํ„ด setBoards(prev => ...) ์„ ์“ฐ๋ฉด, boards๋ฅผ deps์— ๋„ฃ์ง€ ์•Š์•„๋„ ํ•ญ์ƒ ์ตœ์‹  ๊ฐ’์„ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์–ด์„œ []๋กœ ๋‘๋Š” ๊ฒŒ ๊ฐ€๋Šฅํ•˜๋‹ค.


React.memo โ€” ์ปดํฌ๋„ŒํŠธ ์ž์ฒด๋ฅผ ๋ฉ”๋ชจ์ด์ œ์ด์…˜

์–ธ์ œ ์“ฐ๋‚˜?

  • ์ž์‹ ์ปดํฌ๋„ŒํŠธ์— ์ ์šฉ
  • ๋ถ€๋ชจ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง๋ผ๋„, ์ž์‹ ์ด ๋ฐ›์€ props๊ฐ€ ๋ฐ”๋€Œ์ง€ ์•Š์œผ๋ฉด ๋ฆฌ๋ Œ๋”๋ง ๊ฑด๋„ˆ๋œ€

์‚ฌ์šฉ๋ฒ•

// ์ปดํฌ๋„ŒํŠธ ์ •์˜ ํ›„ React.memo๋กœ ๊ฐ์‹ธ์„œ export
export default React.memo(BoardListItem);

useCallback๊ณผ ์„ธํŠธ๋กœ ์จ์•ผ ํšจ๊ณผ๊ฐ€ ์žˆ๋Š” ์ด์œ 

  • React.memo๋Š” props๋ฅผ ์–•์€ ๋น„๊ต(===) ๋กœ ํ™•์ธ
  • ๋ถ€๋ชจ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง๋˜๋ฉด ํ•จ์ˆ˜๊ฐ€ ์ƒˆ๋กœ ์„ ์–ธ โ†’ ์ฐธ์กฐ๊ฐ€ ๋‹ฌ๋ผ์ง โ†’ props๊ฐ€ ๋ฐ”๋€ ๊ฒƒ์œผ๋กœ ํŒ๋‹จ โ†’ ์ž์‹ ๋ฆฌ๋ Œ๋”๋ง ๋ฐœ์ƒ
  • useCallback์œผ๋กœ ํ•จ์ˆ˜ ์ฐธ์กฐ๋ฅผ ์œ ์ง€ํ•ด์•ผ React.memo๊ฐ€ ์ œ๋Œ€๋กœ ๋™์ž‘ํ•จ
// ๋ถ€๋ชจ์—์„œ useCallback์œผ๋กœ ํ•จ์ˆ˜ ์•ˆ์ •ํ™”
const removeBoard = useCallback((bno) => { ... }, []);

// ์ž์‹์—์„œ React.memo๋กœ ๊ฐ์‹ธ๊ธฐ
export default React.memo(BoardListItem);
// โ†’ ์ด์ œ board props๊ฐ€ ์•ˆ ๋ฐ”๋€Œ๋ฉด ์ž์‹์€ ๋ฆฌ๋ Œ๋”๋ง ์•ˆ ๋จ โœ…

์„ธ ๊ฐ€์ง€ ํ›…์˜ ์กฐํ•ฉ ์š”์•ฝ

useMemo     โ†’ ๊ณ„์‚ฐ ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ์–ตํ•ด์„œ ๋ถˆํ•„์š”ํ•œ ์žฌ๊ณ„์‚ฐ ๋ฐฉ์ง€
useCallback โ†’ ํ•จ์ˆ˜๋ฅผ ๊ธฐ์–ตํ•ด์„œ ๋ถˆํ•„์š”ํ•œ ์žฌ์„ ์–ธ ๋ฐฉ์ง€
React.memo  โ†’ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ธฐ์–ตํ•ด์„œ ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง ๋ฐฉ์ง€

๐Ÿ’ก "์ƒํƒœ๋ฅผ ์ผ๋‹ค / ์•ˆ ์ผ๋‹ค" ๊ตฌ๋ถ„ํ•˜๋Š” ๋ฒ•

๊ต์ˆ˜๋‹˜ ์งˆ๋ฌธ ๋Œ€๋น„: ์ด ํ•จ์ˆ˜๊ฐ€ ์ƒํƒœ๋ฅผ ์“ด ๊ฑด์ง€ ์•ˆ ์“ด ๊ฑด์ง€ ์–ด๋–ป๊ฒŒ ํŒ๋‹จํ•˜๋‚˜?

ํ•ต์‹ฌ ๊ธฐ์ค€

useState๋กœ ๋งŒ๋“  ๋ณ€์ˆ˜(boards, bno, btitle ๋“ฑ)๊ฐ€ ํ•จ์ˆ˜ ๋ชธํ†ต ์•ˆ์— ์ง์ ‘ ๋ณด์ด๋ฉด โ†’ ์ƒํƒœ๋ฅผ ์“ด ๊ฒƒ

const [boards, setBoards] = useState([...]);  // โ† ์ƒํƒœ
const [bno, setBno]       = useState(6);      // โ† ์ƒํƒœ
const [btitle, setBtitle] = useState("");     // โ† ์ƒํƒœ

์ด ์ด๋ฆ„๋“ค์ด ํ•จ์ˆ˜ ์•ˆ์— ๋ณด์ด๋ฉด โ†’ ์ƒํƒœ ์”€, ์•ˆ ๋ณด์ด๋ฉด โ†’ ์ƒํƒœ ์•ˆ ์”€


์‹ค์Šต ์ฝ”๋“œ๋กœ ํ•˜๋‚˜์”ฉ ํ™•์ธ

// โœ… ์ƒํƒœ ์•ˆ ์”€ โ†’ deps []
const handleBtitleChange = useCallback(function (event) {
    setBtitle(event.target.value);
    // setBtitle์€ setter โ€” ์ƒํƒœ๋ฅผ "๋ฐ”๊พธ๋Š”" ๊ฒƒ์ด์ง€ "์ฝ๋Š”" ๊ฒŒ ์•„๋‹˜
}, []);
// โœ… ์ƒํƒœ ์•ˆ ์”€ โ†’ deps []
const removeBoard = useCallback((bno) => {
    setBoards(prev => prev.filter(board => board.bno !== bno));
    // boards๋ผ๋Š” ์ด๋ฆ„์ด ์•ˆ ๋ณด์ž„. prev๋กœ ๋ฐ›์•„์˜ค๊ณ  ์žˆ์Œ
}, []);
// โŒ ์ƒํƒœ ์”€ โ†’ useCallback ํšจ๊ณผ ์—†์Œ, ๊ทธ๋ƒฅ ์ผ๋ฐ˜ ํ•จ์ˆ˜๋กœ ๋‘ฌ์•ผ ํ•จ
const addBoard = function() {
    const newBoard = { bno, btitle };    // bno, btitle ์ง์ ‘ ์ฝ์Œ
    setBoards([...boards, newBoard]);    // boards ์ง์ ‘ ์ฝ์Œ
    setBno(bno + 1);                     // bno ์ง์ ‘ ์ฝ์Œ
}
// โ†’ deps์— [bno, btitle, boards] ๋‹ค ๋„ฃ์–ด์•ผ ํ•ด์„œ
//   ๊ฒฐ๊ตญ ๋งค๋ฒˆ ์ƒˆ๋กœ ๋งŒ๋“ค์–ด์ง โ†’ useCallback ์˜๋ฏธ ์—†์Œ
//   ๊ทธ๋ž˜์„œ ๊ต์ˆ˜๋‹˜์ด "์ด๊ฑด ๊ทธ๋ƒฅ ๋†”๋‘๋Š” ๊ฒŒ ์ข‹๋‹ค"๊ณ  ํ•˜์‹  ๊ฒƒ
// โŒ ์ƒํƒœ ์”€ โ†’ deps์— [boards] ํ•„์š”
const getLength1 = useCallback(function () {
    return boards.length;   // boards ์ง์ ‘ ์ฝ์Œ
}, [boards]);

ํ—ท๊ฐˆ๋ฆฌ๋Š” ์ผ€์ด์Šค โ€” prev ํŒจํ„ด

// โŒ ์ƒํƒœ ์”€ โ€” boards ์ด๋ฆ„์ด ์ง์ ‘ ๋ณด์ž„
setBoards(boards.filter(...));

// โœ… ์ƒํƒœ ์•ˆ ์”€ โ€” prev๋กœ ๋ฐ›์•„์˜ค๋‹ˆ๊นŒ boards ์ด๋ฆ„ ์—†์Œ
setBoards(prev => prev.filter(...));

prev๋Š” React๊ฐ€ ์•Œ์•„์„œ ์ตœ์‹  ์ƒํƒœ๊ฐ’์„ ๋„ฃ์–ด์ฃผ๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๋‹ค. ๋‚ด๊ฐ€ ์ƒํƒœ๋ฅผ ์ง์ ‘ ์ฝ์€ ๊ฒŒ ์•„๋‹˜.


ํŒ๋‹จ ๊ณต์‹

ํ•จ์ˆ˜ ๋ชธํ†ต ์•ˆ์— ์ƒํƒœ ๋ณ€์ˆ˜ ์ด๋ฆ„(boards, bno, btitle...)์ด ๋ณด์ด๋ฉด?

  YES โ†’ ์ƒํƒœ ์”€ โ†’ deps์— ๊ทธ ์ƒํƒœ ๋„ฃ๊ธฐ
  NO  โ†’ ์ƒํƒœ ์•ˆ ์”€ โ†’ deps []

โš ๏ธ setter๋Š” ์˜ˆ์™ธ: setBoards, setBno ๊ฐ™์€ setter ํ•จ์ˆ˜๋Š” ์ฝ๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ๋ฐ”๊พธ๋Š” ๊ฒƒ.
React๊ฐ€ ํ•ญ์ƒ ์•ˆ์ •์ ์œผ๋กœ ๋ณด์žฅํ•˜๋ฏ€๋กœ deps์— ๋„ฃ์ง€ ์•Š์•„๋„ ๋จ.


๐Ÿ’ก useCallback + React.memo ๊นŠ์ด ์ดํ•ดํ•˜๊ธฐ

์ˆ˜์—… ์ค‘ ํ—ท๊ฐˆ๋ ธ๋˜ ๋ถ€๋ถ„์„ ํ๋ฆ„ ์ˆœ์„œ๋Œ€๋กœ ๋‹ค์‹œ ์ •๋ฆฌํ•œ ๋‚ด์šฉ

1๋‹จ๊ณ„ โ€” ๋ฌธ์ œ ์ƒํ™ฉ: ์™œ ํ•จ์ˆ˜ ์ฃผ์†Œ๊ฐ€ ๋ฌธ์ œ๊ฐ€ ๋˜๋Š”๊ฐ€?

React๋Š” state๊ฐ€ ๋ฐ”๋€Œ๋ฉด ์ปดํฌ๋„ŒํŠธ ํ•จ์ˆ˜๋ฅผ ํ†ต์งธ๋กœ ๋‹ค์‹œ ์‹คํ–‰ํ•œ๋‹ค.

function BoardList() {
    const [btitle, setBtitle] = useState("");  // ์ด๊ฒŒ ๋ฐ”๋€Œ๋ฉด

    // โ†“ ์ด ํ•จ์ˆ˜๋„ ์ƒˆ๋กœ ๋งŒ๋“ค์–ด์ง
    const removeBoard = function(bno) { ... };

    // โ†“ ์ด ํ•จ์ˆ˜๋„ ์ƒˆ๋กœ ๋งŒ๋“ค์–ด์ง
    const changeBoard = function(bno) { ... };
}

btitle(์ œ๋ชฉ ์ž…๋ ฅ์ฐฝ)์— ๊ธ€์ž ํ•˜๋‚˜ ์น  ๋•Œ๋งˆ๋‹ค removeBoard, changeBoard๊ฐ€ ์ƒˆ๋กœ ๋งŒ๋“ค์–ด์ง„๋‹ค.


2๋‹จ๊ณ„ โ€” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ํ•จ์ˆ˜๋Š” "๋ฌผ๊ฑด(๊ฐ์ฒด)"์ด๋‹ค

ํ•จ์ˆ˜๋ฅผ ๋ณ€์ˆ˜์— ๋‹ด์œผ๋ฉด, ๊ทธ ๋ณ€์ˆ˜์—” ํ•จ์ˆ˜์˜ ์ฃผ์†Œ(์ฐธ์กฐ) ๊ฐ€ ๋“ค์–ด๊ฐ„๋‹ค.

const fn1 = function() { return 1; };
const fn2 = function() { return 1; };

console.log(fn1 === fn2);  // โŒ false โ† ๋‚ด์šฉ์ด ๊ฐ™์•„๋„ ๋‹ค๋ฅธ ๋ฌผ๊ฑด!

๋‚ด์šฉ์ด ์™„์ „ํžˆ ๋˜‘๊ฐ™์•„๋„, ์ƒˆ๋กœ ๋งŒ๋“ค์–ด์ง„ ํ•จ์ˆ˜๋Š” ๋‹ค๋ฅธ ์ฃผ์†Œ๋ฅผ ๊ฐ€์ง„๋‹ค.
๋ฆฌ๋ Œ๋”๋ง๋งˆ๋‹ค ํ•จ์ˆ˜๊ฐ€ ์ƒˆ๋กœ ๋งŒ๋“ค์–ด์ง„๋‹ค = ๋งค๋ฒˆ ์ฃผ์†Œ๊ฐ€ ๋‹ฌ๋ผ์ง„๋‹ค.


3๋‹จ๊ณ„ โ€” ์ž์‹ํ•œํ…Œ ๋ฌด์Šจ ์ผ์ด ์ƒ๊ธฐ๋‚˜?

{boards.map((board) => (
    <BoardListItem
        board={board}
        removeBoard={removeBoard}   // โ† btitle ๋ฐ”๋€” ๋•Œ๋งˆ๋‹ค ์ƒˆ ํ•จ์ˆ˜ ์ „๋‹ฌ
        changeBoard={changeBoard}   // โ† btitle ๋ฐ”๋€” ๋•Œ๋งˆ๋‹ค ์ƒˆ ํ•จ์ˆ˜ ์ „๋‹ฌ
    />
))}

๊ธ€์ž ํ•˜๋‚˜ ์ž…๋ ฅํ•  ๋•Œ๋งˆ๋‹ค ์ผ์–ด๋‚˜๋Š” ์ผ:

1. BoardList ๋ฆฌ๋ Œ๋”๋ง
2. removeBoard ์ƒˆ๋กœ ๋งŒ๋“ค์–ด์ง (์ฃผ์†Œ ๋ฐ”๋€œ)
3. changeBoard ์ƒˆ๋กœ ๋งŒ๋“ค์–ด์ง (์ฃผ์†Œ ๋ฐ”๋€œ)
4. BoardListItem์ด ์ƒˆ props๋ฅผ ๋ฐ›์Œ
5. props๊ฐ€ ๋ฐ”๋€Œ์—ˆ์œผ๋‹ˆ๊นŒ โ†’ BoardListItem ์ „๋ถ€ ๋ฆฌ๋ Œ๋”๋ง ๐Ÿ˜ฑ

๊ฒŒ์‹œ๋ฌผ์ด 100๊ฐœ๋ฉด ๊ธ€์ž ํ•˜๋‚˜ ์น  ๋•Œ๋งˆ๋‹ค 100๋ฒˆ ๋ฆฌ๋ Œ๋”๋ง์ด ์ผ์–ด๋‚œ๋‹ค.


4๋‹จ๊ณ„ โ€” useCallback์œผ๋กœ ํ•จ์ˆ˜ ์ฃผ์†Œ ๊ณ ์ •ํ•˜๊ธฐ

const removeBoard = useCallback((bno) => {
    setBoards(prev => prev.filter(board => board.bno !== bno));
}, []);  // โ† ๋นˆ ๋ฐฐ์—ด = ๋งˆ์šดํŠธ ๋•Œ ๋”ฑ ํ•œ ๋ฒˆ๋งŒ ๋งŒ๋“ค๊ณ  ๊ทธ ์ฃผ์†Œ ์œ ์ง€

useCallback์€ ๋ฆฌ์•กํŠธํ•œํ…Œ ์ด๋ ‡๊ฒŒ ๋งํ•˜๋Š” ๊ฒƒ:

"์ด ํ•จ์ˆ˜, ๋ฆฌ๋ Œ๋”๋ง๋ผ๋„ ๋ฒ„๋ฆฌ์ง€ ๋ง๊ณ  ์ฒ˜์Œ ๋งŒ๋“  ๊ฑฐ ๊ณ„์† ์จ."

๊ทธ๋Ÿฌ๋ฉด removeBoard์˜ ์ฃผ์†Œ๊ฐ€ ๋ณ€ํ•˜์ง€ ์•Š๋Š”๋‹ค.

1. BoardList ๋ฆฌ๋ Œ๋”๋ง
2. removeBoard โ†’ useCallback์ด ์ด์ „ ํ•จ์ˆ˜ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ (์ฃผ์†Œ ๋™์ผ)
3. BoardListItem์ด ๋ฐ›๋Š” props โ†’ ์ด์ „์ด๋ž‘ ๊ฐ™์Œ
4. ๋ฆฌ๋ Œ๋”๋ง ์•ˆ ํ•ด๋„ ๋จ! ๐ŸŽ‰

์˜์กด์„ฑ ๋ฐฐ์—ด [] โ€” ์–ธ์ œ ๋ญ˜ ๋„ฃ๋ƒ?

// ๋นˆ ๋ฐฐ์—ด โ†’ ๋งˆ์šดํŠธ ์‹œ ๋”ฑ 1๋ฒˆ๋งŒ ํ•จ์ˆ˜ ์ƒ์„ฑ
const removeBoard = useCallback((bno) => { ... }, []);

// [boards] โ†’ boards๊ฐ€ ๋ฐ”๋€” ๋•Œ๋งŒ ํ•จ์ˆ˜ ์ƒˆ๋กœ ์ƒ์„ฑ
const getLength = useCallback(() => {
    return boards.length;  // boards๋ฅผ ์ง์ ‘ ์ฝ์œผ๋‹ˆ๊นŒ deps์— ๋„ฃ์–ด์•ผ ํ•จ
}, [boards]);

setBoards(prev => ...) ํŒจํ„ด์„ ์“ฐ๋ฉด boards๋ฅผ ์ง์ ‘ ์ฝ์ง€ ์•Š์œผ๋ฏ€๋กœ []๋กœ ๋‘ฌ๋„ ๋œ๋‹ค.

// โŒ boards๋ฅผ ์ง์ ‘ ์ฐธ์กฐ โ†’ deps์— boards ํ•„์š”
const removeBoard = useCallback((bno) => {
    setBoards(boards.filter(b => b.bno !== bno));  // boards ์ง์ ‘ ์ฝ์Œ
}, [boards]);

// โœ… prev๋กœ ๋ฐ›์•„์˜ค๋ฉด โ†’ deps ๋นˆ ๋ฐฐ์—ด ๊ฐ€๋Šฅ
const removeBoard = useCallback((bno) => {
    setBoards(prev => prev.filter(b => b.bno !== bno));  // boards ์•ˆ ์ฝ์Œ
}, []);

5๋‹จ๊ณ„ โ€” React.memo๋กœ ๋ฆฌ๋ Œ๋”๋ง ์ž์ฒด๋ฅผ ๋ง‰๊ธฐ

export default React.memo(BoardListItem);

React.memo๋Š” ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ์‹ธ์„œ ์ด๋ ‡๊ฒŒ ๋งํ•˜๋Š” ๊ฒƒ:

"๋‚˜ํ•œํ…Œ ์˜ค๋Š” props๊ฐ€ ์ด์ „์ด๋ž‘ ๊ฐ™์œผ๋ฉด, ๋ฆฌ๋ Œ๋”๋ง ์•ˆ ํ•ด๋„ ๋ผ."

props๋ฅผ ๋น„๊ตํ•˜๋Š” ๋ฐฉ์‹์ด === ์–•์€ ๋น„๊ต๋‹ค.

์ด์ „ props: { board: {...}, removeBoard: ํ•จ์ˆ˜A, changeBoard: ํ•จ์ˆ˜B }
์ƒˆ props:   { board: {...}, removeBoard: ํ•จ์ˆ˜A, changeBoard: ํ•จ์ˆ˜B }

ํ•จ์ˆ˜A === ํ•จ์ˆ˜A ? โ†’ true (๊ฐ™์€ ์ฃผ์†Œ)
โ†’ props ์•ˆ ๋ฐ”๋€œ โ†’ ๋ฆฌ๋ Œ๋”๋ง ๊ฑด๋„ˆ๋œ€ โœ…

6๋‹จ๊ณ„ โ€” useCallback ์—†์ด React.memo๋งŒ ์“ฐ๋ฉด ์™œ ์•ˆ ๋˜๋‚˜?

// useCallback ์—†์ด
const removeBoard = function(bno) { ... };  // ๋งค ๋ Œ๋”๋ง๋งˆ๋‹ค ์ƒˆ ์ฃผ์†Œ

// React.memo๊ฐ€ ๋น„๊ตํ•  ๋•Œ
์ด์ „ props: { removeBoard: ํ•จ์ˆ˜A }
์ƒˆ props:   { removeBoard: ํ•จ์ˆ˜B }  // ๋‚ด์šฉ ๊ฐ™์•„๋„ ์ฃผ์†Œ๊ฐ€ ๋‹ค๋ฆ„!

ํ•จ์ˆ˜A === ํ•จ์ˆ˜B ? โ†’ false
โ†’ props ๋ฐ”๋€ ๊ฒƒ์œผ๋กœ ํŒ๋‹จ โ†’ ๋ฆฌ๋ Œ๋”๋ง ๋ฐœ์ƒ ๐Ÿ’ฅ

React.memo๋Š” "๋น„๊ตํ•˜๋Š” ๋ฌธ์ง€๊ธฐ"๊ณ , useCallback์€ "์ฃผ์†Œ๋ฅผ ์œ ์ง€์‹œ์ผœ์ฃผ๋Š” ๊ฒƒ"์ด๋‹ค.
๋ฌธ์ง€๊ธฐ ํ˜ผ์ž์„  ์•„๋ฌด ์˜๋ฏธ ์—†๋‹ค. ๋‘˜์€ ์„ธํŠธ๋กœ ์จ์•ผ ํ•œ๋‹ค.


์ „์ฒด ํ๋ฆ„ ํ•œ ๋ฒˆ์— ๋ณด๊ธฐ

๐Ÿ“ ์ œ๋ชฉ ์ž…๋ ฅ์ฐฝ์— ๊ธ€์ž ์ž…๋ ฅ
        โ†“
๐Ÿ”„ BoardList ๋ฆฌ๋ Œ๋”๋ง
        โ†“
  useMemo    โ†’ boards ์•ˆ ๋ฐ”๋€Œ์—ˆ์œผ๋‹ˆ โ†’ ์ด์ „ ๊ณ„์‚ฐ๊ฐ’ ๊ทธ๋Œ€๋กœ โœ…
  useCallback โ†’ removeBoard ํ•จ์ˆ˜ ์ฃผ์†Œ ๊ทธ๋Œ€๋กœ โœ…
  useCallback โ†’ changeBoard ํ•จ์ˆ˜ ์ฃผ์†Œ ๊ทธ๋Œ€๋กœ โœ…
        โ†“
  BoardListItem์— ์ „๋‹ฌ๋˜๋Š” props โ†’ ์ด์ „์ด๋ž‘ ๊ฐ™์Œ
        โ†“
  React.memo โ†’ props ์•ˆ ๋ฐ”๋€Œ์—ˆ์œผ๋‹ˆ โ†’ ๋ฆฌ๋ Œ๋”๋ง ๊ฑด๋„ˆ๋œ€ โœ…

์ตœ์ข… ํ•œ ์ค„ ์š”์•ฝ

ํ›…๋ฌด์—‡์„ ๊ธฐ์–ต?๋ชฉ์ 
useMemo๊ณ„์‚ฐ ๊ฒฐ๊ณผ๊ฐ’๋ฌด๊ฑฐ์šด ๊ณ„์‚ฐ ๋ฐ˜๋ณต ๋ฐฉ์ง€
useCallbackํ•จ์ˆ˜์˜ ์ฃผ์†Œ์ž์‹ํ•œํ…Œ ๋„˜๊ธธ ํ•จ์ˆ˜ ์ฃผ์†Œ ์œ ์ง€
React.memo์ปดํฌ๋„ŒํŠธ ๋ Œ๋” ๊ฒฐ๊ณผprops ์•ˆ ๋ฐ”๋€Œ๋ฉด ๋ฆฌ๋ Œ๋”๋ง ๊ฑด๋„ˆ๋œ€

๐Ÿ› ์˜ค๋Š˜ ๋ฐœ์ƒํ•œ ์—๋Ÿฌ ์ •๋ฆฌ

Error 1: Element type is invalid

Uncaught Error: Element type is invalid: expected a string (for built-in components)
or a class/function (for composite components) but got: object.
Check the render method of `BoardList`.

์›์ธ

์ž„ํฌํŠธํ•œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ undefined ๋˜๋Š” object๋กœ ๋“ค์–ด์˜จ ๊ฒฝ์šฐ.

์ฃผ์š” ์›์ธ ํŒจํ„ด

// โŒ export default์ธ๋ฐ named import๋กœ ๊ฐ€์ ธ์˜จ ๊ฒฝ์šฐ
import { BoardListItem } from './BoardListItem';  // undefined๊ฐ€ ๋จ

// โœ… ์˜ฌ๋ฐ”๋ฅธ import
import BoardListItem from './BoardListItem';

// โŒ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ†ต์งธ๋กœ importํ•œ ๊ฒฝ์šฐ
import ReactDOM from 'react-dom';   // object โ†’ ์ปดํฌ๋„ŒํŠธ๋กœ ์‚ฌ์šฉ ๋ถˆ๊ฐ€

// โœ… ์˜ฌ๋ฐ”๋ฅธ import
import { createRoot } from 'react-dom';

๋””๋ฒ„๊น… ๋ฐฉ๋ฒ•

// return ์ง์ „์— console.log๋กœ ์ž„ํฌํŠธ๋œ ๊ฐ’ ํ™•์ธ
console.log({ BoardListItem });
// {} ๋˜๋Š” undefined๋กœ ์ฐํžˆ๋Š” ๊ฒŒ ๋ฒ”์ธ

Error 2: Cannot read properties of undefined (reading 'length')

์›์ธ

useCallback ์•ˆ์—์„œ setBoards์— return์„ ๋น ๋œจ๋ ค์„œ undefined๊ฐ€ ์ „๋‹ฌ๋จ.

// โŒ return์ด ์—†์–ด์„œ setBoards์— undefined ์ „๋‹ฌ
const changeBoard = useCallback((bno) => {
  setBoards(aa => {
    aa.map(board => {          // map ๊ฒฐ๊ณผ๋ฅผ return ์•ˆ ํ•จ!
      if (board.bno === bno) {
        return { ...board, btitle: board.btitle + " (๋ณ€๊ฒฝ)" };
      } else {
        return board;
      }
    });                        // ์—ฌ๊ธฐ์„œ return์ด ์—†์Œ
  });
}, [boards]);

// โœ… ์˜ฌ๋ฐ”๋ฅธ ์ฝ”๋“œ
const changeBoard = useCallback((bno) => {
  setBoards(aa => {
    return aa.map(board => {   // return ์ถ”๊ฐ€
      if (board.bno === bno) {
        return { ...board, btitle: board.btitle + " (๋ณ€๊ฒฝ)" };
      } else {
        return board;
      }
    });
  });
}, []);

ํ•ต์‹ฌ ํฌ์ธํŠธ

์ค‘๊ด„ํ˜ธ {} ์‚ฌ์šฉ ์—ฌ๋ถ€return ํ•„์š” ์—ฌ๋ถ€
ํ™”์‚ดํ‘œ ํ•จ์ˆ˜ () => ๊ฐ’return ๋ถˆํ•„์š” (์•”๋ฌต์  ๋ฐ˜ํ™˜)
ํ™”์‚ดํ‘œ ํ•จ์ˆ˜ () => { ... }return ๋ช…์‹œ ํ•„์ˆ˜

๐Ÿ’ก setBoards(prev => prev.map(...)) ์ฒ˜๋Ÿผ ์ค‘๊ด„ํ˜ธ ์—†์ด ์“ฐ๋ฉด ์‹ค์ˆ˜๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค.


๋

profile
์•ˆ๋…•ํ•˜์„ธ์š”

0๊ฐœ์˜ ๋Œ“๊ธ€