๐Ÿ’ป๐Ÿ’€/๊ฐœ๋ฐœ

[React] ์„ฑ๋Šฅ ์ตœ์ ํ™”:: React.memo๋ฅผ ์‚ฌ์šฉํ•œ ์ปดํฌ๋„ŒํŠธ Memoization

db2 2021. 9. 15. 14:59

๋ฆฌ์•กํŠธ์—์„œ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™” ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์—๋Š” ์—ฌ๋Ÿฌ ๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค. ์ด๋ฒˆ์—” ๊ทธ ์ค‘์—์„œ๋„ ๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅํ•˜์—ฌ(memoization) ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ฅผ ๋ฐฉ์ง€ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ฒ ๋‹ค.

Memoization์ด๋ž€

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

๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ์˜ Memoization

๊ธฐ๋ณธ์ ์œผ๋กœ ๋ฆฌ์•กํŠธ๋Š”

  1. ๋‚ด๋ถ€ state๋‚˜ ์ „๋‹ฌ๋ฐ›์€ props๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ
  2. ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋” ๋  ๋•Œ
  3. shouldComponentUpdate() ๋ฉ”์„œ๋“œ์˜ ๋ฆฌํ„ด๊ฐ’ ํ˜น์€ React.memo ์˜ ๋‘๋ฒˆ์งธ ์ธ์ž๋กœ ์ „๋‹ฌํ•œ ๋น„๊ต ํ•จ์ˆ˜(ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ)์˜ ๋ฆฌํ„ด๊ฐ’์ด true์ผ ๋•Œ
  4. ๊ฐ•์ œ ๋ฆฌ๋ Œ๋”๋ฅผ ํ•  ๋•Œ

state์™€ props์˜ ์—…๋ฐ์ดํŠธ ์—ฌ๋ถ€๋ฅผ Object.is() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ์–•์€ ๋น„๊ต(๋ฐ์ดํ„ฐ๊ฐ€ ๊ฐ์ฒด, ๋ฐฐ์—ด, ํ•จ์ˆ˜์™€ ๊ฐ™์€ ์ฐธ์กฐํ˜•์ธ ๊ฒฝ์šฐ ๋ฐ์ดํ„ฐ ์ž์ฒด๊ฐ€ ์•„๋‹Œ, ๋ฐ์ดํ„ฐ๊ฐ€ ์ €์žฅ๋œ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๊ฐ’์„ ๋น„๊ต)ํ•˜์—ฌ ์ปดํฌ๋„ŒํŠธ ๋ฆฌ๋ Œ๋”๋ง์„ ํ•œ๋‹ค.

ํ•ญ๋ชฉ์„ ํด๋ฆญํ•˜๋ฉด ์ทจ์†Œ์„ ์ด ๊ทธ์–ด์ง€๋Š” ๊ฐ„๋‹จํ•œ To-Do ๋ฆฌ์ŠคํŠธ๋ฅผ ์˜ˆ๋กœ ๋“ค์–ด๋ณด์ž.

import React, { useState } from "react";
import "./styles.css";

export default function App() {
  const [list, setList] = useState([
    {
      id: 1,
      title: "item 1",
      checked: true
    },
    {
      id: 2,
      title: "item 2",
      checked: false
    },
    {
      id: 3,
      title: "item 3",
      checked: false
    }
  ]);

  const handleClick = (id) => {
    setList(
      list.map((item) =>
        item.id === id ? { ...item, checked: !item.checked } : item
      )
    );
  };

  return <List list={list} handleClick={handleClick} />;
}

function List({ list, handleClick }) {
  return (
    <ul>
      {list.map((item) => (
        <Item key={item.id} item={item} handleClick={handleClick} />
      ))}
    </ul>
  );
}

function Item({ item, handleClick }) {
  console.log("re-render", item.title);

  return (
    <li
      className={item.checked ? "checked" : undefined}
      onClick={() => handleClick(item.id)}
    >
      {item.title}
    </li>
  );
}

์•„์ง์€ ๋ฉ”๋ชจ์ด์ œ์ด์…˜์„ ์œ„ํ•œ ์ฒ˜๋ฆฌ๋ฅผ ํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— ์ฒซ ๋ฒˆ์งธ ํ•ญ๋ชฉ์„ ํด๋ฆญํ–ˆ์Œ์—๋„ ์•„๋ž˜์ฒ˜๋Ÿผ ๋ชจ๋“  ํ•ญ๋ชฉ๋“ค์ด ๋ฆฌ๋ Œ๋”๋ง ๋˜์–ด ์ฝ˜์†”์— ์ฐํžˆ๋Š” ์ƒํƒœ์ด๋‹ค.

์ด๋Š” ์ตœ์ƒ์œ„ ๋ถ€๋ชจ App ์ปดํฌ๋„ŒํŠธ์—์„œ Item ์ปดํฌ๋„ŒํŠธ์˜ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ฒซ ๋ฒˆ์งธ ํ•ญ๋ชฉ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋œ ๊ฒƒ์€ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์˜ state๊ฐ€ ๋ณ€๊ฒฝ๋œ ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์—, ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋” ๋จ์— ๋”ฐ๋ผ ๋ชจ๋“  ์ž์‹ Item ์ปดํฌ๋„ŒํŠธ๋“ค์ด ๋ฆฌ๋ Œ๋”๋ง ๋˜๋Š” ๊ฒƒ์ด๋‹ค. ์ด์ฒ˜๋Ÿผ state๋‚˜ props๊ฐ€ ๋ณ€ํ•˜์ง€ ์•Š์•˜์Œ์—๋„ ๋ฆฌ๋ Œ๋”๋ง ๋˜๋Š” ๊ฒƒ์€ ์กฐ๊ธˆ ๋ถˆํ•„์š”ํ•ด ๋ณด์ธ๋‹ค. ๋งŒ์•ฝ ์˜ˆ์‹œ์™€ ๊ฐ™์ด ์•„์ฃผ ์ž‘์€ ๊ทœ๋ชจ๊ฐ€ ์•„๋‹ˆ๋ผ ํ•ญ๋ชฉ์ด ์ˆ˜ ์ฒœ, ์ˆ˜ ๋งŒ ๊ฐœ๊ฐ€ ๋˜๋Š” ๋ฌด๊ฑฐ์šด ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์ด๋ผ๋ฉด ์„ฑ๋Šฅ์—๋„ ์˜ํ–ฅ์„ ๋ผ์น  ๊ฒƒ์ด๋‹ค.

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

React.memo

React.memo๋Š” ์ปดํฌ๋„ŒํŠธ์˜ ๋ Œ๋”๋ง ๊ฒฐ๊ณผ๋ฅผ ๋ฉ”๋ชจ์ด์ œ์ด์…˜ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” ๊ณ ์ฐจ ์ปดํฌ๋„ŒํŠธ(HOC)์ด๋‹ค. ๋ณ€ํ™˜๋œ ์ปดํฌ๋„ŒํŠธ๋Š” ์ „๋‹ฌ๋ฐ›์€ props์˜ ์–•์€ ๋น„๊ต๋ฅผ ํ†ตํ•ด, ์ „๊ณผ ๊ฐ™๋‹ค๋ฉด ์ด์ „ ๋ Œ๋”๋ง ๊ฒฐ๊ณผ๋ฅผ ์žฌ์‚ฌ์šฉํ•œ๋‹ค.

์•„๋ž˜์ฒ˜๋Ÿผ React.memo์˜ ์ฒซ ๋ฒˆ์งธ ์ธ์ž๋กœ ๋ฉ”๋ชจ์ด์ œ์ด์…˜ ํ•˜๊ณ  ์‹ถ์€ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ „๋‹ฌํ•œ๋‹ค. ๋งŒ์•ฝ ์–•์€ ๋น„๊ต๊ฐ€ ์•„๋‹Œ ๋‹ค๋ฅธ ๋น„๊ต๋ฅผ ์›ํ•œ๋‹ค๋ฉด, ๋‘ ๋ฒˆ์งธ ์ธ์ž๋กœ ๋ณ„๋„๋กœ ์ž‘์„ฑํ•œ ๋น„๊ต ํ•จ์ˆ˜๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.

function ComponentToBeMemoized(props) {
    // ๋ Œ๋”๋ง
}

function areEqual(prevProps, nextProps) {
    // ๋น„๊ต ๋กœ์ง
}

export default React.memo(ComponentToBeMemoized, areEqual);

areEqual ํ•จ์ˆ˜๋Š” props๋“ค์ด ๊ฐ™์œผ๋ฉด true๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ , ๋‹ค๋ฅด๋ฉด false๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๋น„๊ต ํ•จ์ˆ˜์˜ ๋ฆฌํ„ด๊ฐ’์ด true์ผ ๋•Œ๋Š” props๊ฐ€ ๋ณ€ํ•˜์ง€ ์•Š์•˜๋‹ค๊ณ  ํŒ๋‹จํ•˜์—ฌ ์ด์ „ ๊ฒฐ๊ณผ๋ฅผ ์žฌ์‚ฌ์šฉํ•˜๋„๋ก ํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค.

๋ฐ˜๋ฉด์— ํด๋ž˜์Šคํ˜• ์ปดํฌ๋„ŒํŠธ์˜ shouldComponentUpdate() ๋ฉ”์„œ๋“œ๋Š” props๋“ค์ด ๊ฐ™์œผ๋ฉด false๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ , ๋‹ค๋ฅด๋ฉด true๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ๋ฆฌํ„ด๊ฐ’์ด true ์ผ ๋•Œ props๊ฐ€ ๋‹ฌ๋ผ ์ƒˆ๋กญ๊ฒŒ ์—ฐ์‚ฐํ•ด์•ผ ํ•œ๋‹ค๊ณ  ํŒ๋‹จํ•˜์—ฌ ๋ฆฌ๋ Œ๋”๋ง ํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค. (ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ์™€ ์ •๋ฐ˜๋Œ€์˜ ๋ฐฉ์‹์ด๋‹ˆ ์œ ์˜ํ•  ๊ฒƒโ˜ )

๊ทธ๋Ÿผ ์ด์ œ Item ์ปดํฌ๋„ŒํŠธ์— ์ ์šฉํ•ด๋ณด์ž. ์ปดํฌ๋„ŒํŠธ๋ฅผ ์„ ์–ธํ•˜๋ฉด์„œ ๋™์‹œ์— React.memo๋ฅผ ์ ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์•„๋ž˜์ฒ˜๋Ÿผ ํ•จ์ˆ˜ ํ‘œํ˜„์‹์œผ๋กœ ์ž‘์„ฑํ•œ๋‹ค.

const Item = React.memo(({ item, handleClick }) => {
  console.log("re-render", item.title);

  return (
    <li
      className={item.checked ? "checked" : undefined}
      onClick={() => handleClick(item.id)}
    >
      {item.title}
    </li>
  );
});

ํ•˜์ง€๋งŒ ์•„์ง๋„ ์—ฌ์ „ํžˆ ๋ชจ๋“  ํ•ญ๋ชฉ๋“ค์ด ๋ฆฌ๋ Œ๋”๋ง ๋œ๋‹ค?! ์ •์ƒ์ด๋‹ค. ๊ทธ ์›์ธ์€ ํ•จ์ˆ˜์˜ ํŠน์ง•์— ์žˆ๋‹ค.

ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง ๋œ๋‹ค๋Š” ๊ฑด ํ•ด๋‹น ํ•จ์ˆ˜๊ฐ€ ๋‹ค์‹œ ํ˜ธ์ถœ๋๋‹ค๋Š” ๋œป์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  ํ•จ์ˆ˜๋Š” ํ˜ธ์ถœ๋˜์–ด ์‹คํ–‰๋  ๋•Œ๋งˆ๋‹ค ๋‚ด๋ถ€์— ์„ ์–ธ๋œ ๋ณ€์ˆ˜๋“ค๊ณผ ํ•จ์ˆ˜๋“ค ๋˜ํ•œ ๋‹ค์‹œ ์„ ์–ธํ•˜์—ฌ ์ƒˆ๋กœ์šด ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๋ฅผ ํ• ๋‹นํ•˜๋Š” ํŠน์ง•์„ ๊ฐ€์ง„๋‹ค. ์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒ์„ฑ๋œ ์ดํ›„์—๋Š” ๋ฆฌ๋ Œ๋”๋ง์ด ๋˜์–ด๋„ ๋ฉค๋ฒ„ ๋ณ€์ˆ˜๋‚˜ ๋ฉ”์„œ๋“œ๋“ค์ด ์žฌ์„ ์–ธ ๋˜์ง€ ์•Š๋Š” ํด๋ž˜์Šค์™€๋Š” ๋น„๊ต๋œ๋‹ค.

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

โš  React.memo๋Š” props ๋ณ€ํ™”์—๋งŒ ์˜ํ–ฅ์„ ๋ฐ›๋Š”๋‹ค. ๋งŒ์•ฝ React.memo๋กœ ๊ฐ์‹ธ์ง„ ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€์—์„œ useState, useReducer, useContext Hook์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด, state๋‚˜ context๊ฐ€ ๋ณ€ํ•  ๋•Œ๋งˆ๋‹ค ์œ„์— ์–ธ๊ธ‰ํ•œ ๋ฆฌ๋ Œ๋” ์กฐ๊ฑด(1. ๋‚ด๋ถ€ state๋‚˜ ์ „๋‹ฌ๋ฐ›์€ props๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ)์— ๋ถ€ํ•ฉํ•˜์—ฌ ๋‹ค์‹œ ๋ Œ๋”๋ง ๋  ๊ฒƒ์ด๋‹ค.

useCallback()

useCallback() Hook์€ ๋ฉ”๋ชจ์ด์ œ์ด์…˜๋œ ์ฝœ๋ฐฑํ•จ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฆฌ์•กํŠธ์˜ ๋‚ด์žฅ API์ด๋‹ค. ์ฒซ ๋ฒˆ์งธ ์ธ์ž๋กœ ํ•จ์ˆ˜๋ฅผ, ๋‘ ๋ฒˆ์งธ ์ธ์ž๋กœ ์˜์กด์„ฑ ๋ฐฐ์—ด์„ ์ „๋‹ฌ๋ฐ›๋Š”๋‹ค.

import React, { useCallback } from 'react';

const memoizedCallback = useCallback(() => {
    // Do something with a
}, [a]);

์˜์กด์„ฑ์ด ๋ณ€๊ฒฝ๋์„ ๋•Œ๋งŒ ๋ณ€๊ฒฝ๋œ ์˜์กด์„ฑ์„ ์ฐธ์กฐํ•˜๋Š” ํ•จ์ˆ˜๊ฐ€ ์ƒˆ๋กญ๊ฒŒ ์„ ์–ธ๋˜์–ด ๋ฐ˜ํ™˜๋  ๊ฒƒ์ด๊ณ , ๊ทธ๋ ‡์ง€ ์•Š๋‹ค๋ฉด ๋ฉ”๋ชจ์ด์ œ์ด์…˜ ๋œ ํ•จ์ˆ˜๊ฐ€ ์žฌ์‚ฌ์šฉ๋  ๊ฒƒ์ด๋‹ค.

const handleClick = useCallback(
  (id) => {
    setList(
      list.map((item) =>
        item.id === id ? { ...item, checked: !item.checked } : item
      )
    );
  },
  [list]
);

ํ•˜์ง€๋งŒ ์•„์ง ํ•จ์ •์ด ํ•˜๋‚˜ ๋” ๋‚จ์•„์žˆ๋‹ค.

์œ„์˜ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด ์˜์กด์„ฑ ๋ฐฐ์—ด๋กœ list๋ฅผ ์ „๋‹ฌํ•˜๊ณ  ์žˆ๋‹ค. list๋Š” ๊ฐ ํ•ญ๋ชฉ์„ ๋ˆ„๋ฅผ ๋•Œ๋งˆ๋‹ค ๋งค๋ฒˆ ๋ฐ”๋€Œ๋Š” ๋ฐ์ดํ„ฐ์ด๋‹ค. ๋”ฐ๋ผ์„œ handleClick ํ•จ์ˆ˜๋„ ๋งค๋ฒˆ ์ƒˆ๋กœ์šด ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ์— ํ• ๋‹น๋  ๊ฒƒ์ด๊ณ , ๊ฒฐ๊ตญ ์ด์ „๊ณผ ๊ฐ™์ด ๋ชจ๋“  Item ์ปดํฌ๋„ŒํŠธ๋“ค์ด ๋ฆฌ๋ Œ๋” ๋  ๊ฒƒ์ด๋‹ค. ์ด ๋ฌธ์ œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ํ•จ์ˆ˜ํ˜• ์—…๋ฐ์ดํŠธ๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

const handleClick = useCallback((id) => {
  setList((list) =>
    list.map((item) =>
      item.id === id ? { ...item, checked: !item.checked } : item
    )
  );
}, []);

์—…๋ฐ์ดํŠธ ํ•จ์ˆ˜๋Š” ์ธ์ž๋กœ ํ•จ์ˆ˜๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜๋„ ์žˆ๋Š”๋ฐ, ์ด ํ•จ์ˆ˜๋Š” ์ž๋™์ ์œผ๋กœ ์ตœ์‹  ์ƒํƒœ๋ฅผ ์ธ์ž๋กœ ์ „๋‹ฌ๋ฐ›๋Š”๋‹ค. ๋”ฐ๋ผ์„œ useCallback() ์˜์กด์„ฑ ๋ฐฐ์—ด์— list๋ฅผ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์•„๋„ ๊ฐ€์žฅ ์ตœ์‹  list๋ฅผ ์ฐธ์กฐํ•˜๋Š” handleClick ํ•จ์ˆ˜๋ฅผ ๋ฉ”๋ชจ์ด์ œ์ด์…˜ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

์ตœ์ข… ์ฝ”๋“œ์ด๋‹ค. ์„ฑ๊ณต์ ์œผ๋กœ Item ์ปดํฌ๋„ŒํŠธ์˜ ๋ฉ”๋ชจ์ด์ œ์ด์…˜์ด ๋˜๊ณ  ์žˆ๋‹ค. ์„ฑ๊ณต์ ๐Ÿ˜Ž๐Ÿ‘