๋ฐฐ์—ด State ์—…๋ฐ์ดํŠธํ•˜๊ธฐ

๋ฐฐ์—ด์€ JavaScript์—์„œ๋Š” ๋ณ€๊ฒฝ์ด ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, state๋กœ ์ €์žฅํ•  ๋•Œ์—๋Š” ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋„๋ก ์ฒ˜๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ์ฒด์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ, state์— ์ €์žฅ๋œ ๋ฐฐ์—ด์„ ์—…๋ฐ์ดํŠธํ•˜๊ณ  ์‹ถ์„ ๋•Œ์—๋Š”, ์ƒˆ ๋ฐฐ์—ด์„ ์ƒ์„ฑ(ํ˜น์€ ๊ธฐ์กด ๋ฐฐ์—ด์˜ ๋ณต์‚ฌ๋ณธ์„ ์ƒ์„ฑ)ํ•œ ๋’ค, ์ด ์ƒˆ ๋ฐฐ์—ด์„ state๋กœ ๋‘์–ด ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํ•™์Šต ๋‚ด์šฉ

  • React state์—์„œ ๋ฐฐ์—ด์˜ ํ•ญ๋ชฉ์„ ์ถ”๊ฐ€, ์‚ญ์ œ ๋˜๋Š” ๋ณ€๊ฒฝํ•˜๋Š” ๋ฐฉ๋ฒ•
  • ๋ฐฐ์—ด ๋‚ด๋ถ€์˜ ๊ฐ์ฒด๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฐฉ๋ฒ•
  • Immer๋กœ ๋œ ๋ฐ˜๋ณตํ•ด์„œ ๋ฐฐ์—ด์„ ๋ณต์‚ฌํ•˜๋Š” ๋ฐฉ๋ฒ•

๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ  ๋ฐฐ์—ด ์—…๋ฐ์ดํŠธํ•˜๊ธฐ

JavaScript์—์„œ ๋ฐฐ์—ด์€ ๋‹ค๋ฅธ ์ข…๋ฅ˜์˜ ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค. ๊ฐ์ฒด์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ React state์—์„œ ๋ฐฐ์—ด์€ ์ฝ๊ธฐ ์ „์šฉ์œผ๋กœ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰ arr[0] = 'bird'์ฒ˜๋Ÿผ ๋ฐฐ์—ด ๋‚ด๋ถ€์˜ ํ•ญ๋ชฉ์„ ์žฌํ• ๋‹นํ•ด์„œ๋Š” ์•ˆ ๋˜๋ฉฐ push()๋‚˜ pop()๊ฐ™์€ ํ•จ์ˆ˜๋กœ ๋ฐฐ์—ด์„ ๋ณ€๊ฒฝํ•ด์„œ๋Š” ์•ˆ๋ฉ๋‹ˆ๋‹ค.

๋Œ€์‹  ๋ฐฐ์—ด์„ ์—…๋ฐ์ดํŠธํ•  ๋•Œ๋งˆ๋‹ค ์ƒˆ ๋ฐฐ์—ด์„ state ์„ค์ • ํ•จ์ˆ˜์— ์ „๋‹ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด state์˜ ์›๋ณธ ๋ฐฐ์—ด์„ ๋ณ€๊ฒฝ์‹œํ‚ค์ง€ ์•Š๋Š” filter()์™€ map() ๊ฐ™์€ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์›๋ณธ ๋ฐฐ์—ด๋กœ๋ถ€ํ„ฐ ์ƒˆ ๋ฐฐ์—ด์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ดํ›„ ์ด ์ƒˆ ๋ฐฐ์—ด๋“ค์„ state์— ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ์€ ์ผ๋ฐ˜์ ์ธ ๋ฐฐ์—ด ์—ฐ์‚ฐ์— ๋Œ€ํ•œ ์ฐธ์กฐ ํ‘œ์ž…๋‹ˆ๋‹ค. React state ๋‚ด์—์„œ ๋ฐฐ์—ด์„ ๋‹ค๋ฃฐ ๋•, ์™ผ์ชฝ ์—ด์— ์žˆ๋Š” ํ•จ์ˆ˜๋“ค์˜ ์‚ฌ์šฉ์„ ํ”ผํ•˜๋Š” ๋Œ€์‹ , ์˜ค๋ฅธ์ชฝ ์—ด์— ์žˆ๋Š” ํ•จ์ˆ˜๋“ค์„ ์„ ํ˜ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋น„์„ ํ˜ธ (๋ฐฐ์—ด์„ ๋ณ€๊ฒฝ)์„ ํ˜ธ (์ƒˆ ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜)
์ถ”๊ฐ€push, unshiftconcat, [...arr] ์ „๊ฐœ ์—ฐ์‚ฐ์ž (์˜ˆ์‹œ)
์ œ๊ฑฐpop, shift, splicefilter, slice (์˜ˆ์‹œ)
๊ต์ฒดsplice, arr[i] = ... ํ• ๋‹นmap (์˜ˆ์‹œ)
์ •๋ ฌreverse, sort๋ฐฐ์—ด์„ ๋ณต์‚ฌํ•œ ์ดํ›„ ์ฒ˜๋ฆฌ (์˜ˆ์‹œ)

๋˜๋Š” ๋‘ ์—ด์˜ ํ•จ์ˆ˜๋ฅผ ๋ชจ๋‘ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” Immer๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฃผ์˜ํ•˜์„ธ์š”!

์•ˆํƒ€๊น์ง€๋งŒ, slice์™€ splice ํ•จ์ˆ˜๋Š” ์ด๋ฆ„์ด ๋น„์Šทํ•˜์ง€๋งŒ ๋ชน์‹œ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

  • slice๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฐฐ์—ด ๋˜๋Š” ๊ทธ ์ผ๋ถ€๋ฅผ ๋ณต์‚ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • splice๋Š” ๋ฐฐ์—ด์„ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค. (ํ•ญ๋ชฉ์„ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.)

React์—์„œ๋Š”, state์˜ ๊ฐ์ฒด๋‚˜ ๋ฐฐ์—ด์„ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๋Š” ๊ฒŒ ์ข‹๊ธฐ ๋•Œ๋ฌธ์— slice (p๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค!)๋ฅผ ํ›จ์”ฌ ๋” ์ž์ฃผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ฐ์ฒด ์—…๋ฐ์ดํŠธ์—์„œ ๋ณ€๊ฒฝ์ด ๋ฌด์—‡์ด๊ณ  ์™œ state์— ๊ถŒ์žฅ๋˜์ง€ ์•Š๋Š”์ง€์— ๋Œ€ํ•ด ์ด์œ ๋ฅผ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

๋ฐฐ์—ด์— ํ•ญ๋ชฉ ์ถ”๊ฐ€ํ•˜๊ธฐ

push()๋Š” ๋ฐฐ์—ด์„ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค. (์›์น˜ ์•Š๋Š” ๋ฐฉ์‹)

import { useState } from 'react';

let nextId = 0;

export default function List() {
  const [name, setName] = useState('');
  const [artists, setArtists] = useState([]);

  return (
    <>
      <h1>Inspiring sculptors:</h1>
      <input
        value={name}
        onChange={e => setName(e.target.value)}
      />
      <button onClick={() => {
        artists.push({
          id: nextId++,
          name: name,
        });
      }}>Add</button>
      <ul>
        {artists.map(artist => (
          <li key={artist.id}>{artist.name}</li>
        ))}
      </ul>
    </>
  );
}

๋Œ€์‹  ๊ธฐ์กด์— ์กด์žฌํ•˜๋˜ ํ•ญ๋ชฉ๋“ค ๋’ค์— ์ƒˆ ํ•ญ๋ชฉ์„ ํฌํ•จํ•˜๋Š” ์ƒˆ๋กœ์šด ๋ฐฐ์—ด์„ ๋งŒ๋“œ์„ธ์š”. ์ด๋ฅผ ์œ„ํ•œ ๋ฐฉ๋ฒ•์€ ์—ฌ๋Ÿฌ ๊ฐ€์ง€๊ฐ€ ์žˆ์ง€๋งŒ ๊ฐ€์žฅ ์‰ฌ์šด ๋ฐฉ๋ฒ•์€ ... ๋ฐฐ์—ด ์ „๊ฐœ ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

setArtists( // ์•„๋ž˜์˜ ์ƒˆ๋กœ์šด ๋ฐฐ์—ด๋กœ state๋ฅผ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.
[
...artists, // ๊ธฐ์กด ๋ฐฐ์—ด์˜ ๋ชจ๋“  ํ•ญ๋ชฉ์—,
{ id: nextId++, name: name } // ๋งˆ์ง€๋ง‰์— ์ƒˆ ํ•ญ๋ชฉ์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
]
);

์ด์ œ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

import { useState } from 'react';

let nextId = 0;

export default function List() {
  const [name, setName] = useState('');
  const [artists, setArtists] = useState([]);

  return (
    <>
      <h1>Inspiring sculptors:</h1>
      <input
        value={name}
        onChange={e => setName(e.target.value)}
      />
      <button onClick={() => {
        setArtists([
          ...artists,
          { id: nextId++, name: name }
        ]);
      }}>Add</button>
      <ul>
        {artists.map(artist => (
          <li key={artist.id}>{artist.name}</li>
        ))}
      </ul>
    </>
  );
}

๋ฐฐ์—ด ์ „๊ฐœ ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ธฐ์กด ๋ฐฐ์—ด์ธ ...artists์˜ ์•ž์— ํ•ญ๋ชฉ์„ ๋ฐฐ์น˜ํ•˜์—ฌ ์ถ”๊ฐ€ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

setArtists([
{ id: nextId++, name: name }, // ์ถ”๊ฐ€ํ•  ํ•ญ๋ชฉ์„ ์•ž์— ๋ฐฐ์น˜ํ•˜๊ณ ,
...artists // ๊ธฐ์กด ๋ฐฐ์—ด์˜ ํ•ญ๋ชฉ๋“ค์„ ๋’ค์— ๋ฐฐ์น˜ํ•ฉ๋‹ˆ๋‹ค.
]);

์ด๋Ÿฐ ์‹์œผ๋กœ ์ „๊ฐœ ๊ตฌ๋ฌธ์€ ๋ฐฐ์—ด์˜ ๊ฐ€์žฅ ๋’ค์— ์ถ”๊ฐ€ํ•˜๋Š” push()์™€, ๋ฐฐ์—ด์˜ ๊ฐ€์žฅ ์•ž์— ์ถ”๊ฐ€ํ•˜๋Š” unshift()์˜ ๋‘ ๊ธฐ๋Šฅ ๋ชจ๋‘ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์œ„์˜ ์ƒŒ๋“œ๋ฐ•์Šค์—์„œ ์‚ฌ์šฉํ•ด๋ณด์„ธ์š”!

๋ฐฐ์—ด์—์„œ ํ•ญ๋ชฉ ์ œ๊ฑฐํ•˜๊ธฐ

๋ฐฐ์—ด์—์„œ ํ•ญ๋ชฉ์„ ์ œ๊ฑฐํ•˜๋Š” ๊ฐ€์žฅ ์‰ฌ์šด ๋ฐฉ๋ฒ•์€ ํ•„ํ„ฐ๋งํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‹ค์‹œ ๋งํ•ด์„œ ํ•ด๋‹น ํ•ญ๋ชฉ์„ ํฌํ•จํ•˜์ง€ ์•Š๋Š” ์ƒˆ ๋ฐฐ์—ด์„ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ ค๋ฉด filter ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

import { useState } from 'react';

let initialArtists = [
  { id: 0, name: 'Marta Colvin Andrade' },
  { id: 1, name: 'Lamidi Olonade Fakeye'},
  { id: 2, name: 'Louise Nevelson'},
];

export default function List() {
  const [artists, setArtists] = useState(
    initialArtists
  );

  return (
    <>
      <h1>Inspiring sculptors:</h1>
      <ul>
        {artists.map(artist => (
          <li key={artist.id}>
            {artist.name}{' '}
            <button onClick={() => {
              setArtists(
                artists.filter(a =>
                  a.id !== artist.id
                )
              );
            }}>
              Delete
            </button>
          </li>
        ))}
      </ul>
    </>
  );
}

โ€Deleteโ€ ๋ฒ„ํŠผ์„ ๋ช‡ ๋ฒˆ ํด๋ฆญํ•˜๊ณ , ํด๋ฆญ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ํ™•์ธํ•ด๋ณด์„ธ์š”.

setArtists(
artists.filter(a => a.id !== artist.id)
);

์—ฌ๊ธฐ์„œ artists.filter(s => s.id !== artist.id)๋Š” โ€œartist.id์™€ ID๊ฐ€ ๋‹ค๋ฅธ artists๋กœ ๊ตฌ์„ฑ๋œ ๋ฐฐ์—ด์„ ์ƒ์„ฑํ•œ๋‹คโ€๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค. ์ฆ‰, ๊ฐ artist์˜ โ€œDeleteโ€ ๋ฒ„ํŠผ์€ ํ•ด๋‹น artist๋ฅผ ๋ฐฐ์—ด์—์„œ ํ•„ํ„ฐ๋งํ•œ ๋‹ค์Œ, ๋ฐ˜ํ™˜๋œ ๋ฐฐ์—ด๋กœ ๋ฆฌ๋ Œ๋”๋ง์„ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค. filter๊ฐ€ ์›๋ณธ ๋ฐฐ์—ด์„ ์ˆ˜์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์— ์ฃผ์˜ํ•˜์„ธ์š”.

๋ฐฐ์—ด ๋ณ€ํ™˜ํ•˜๊ธฐ

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

์ด ์˜ˆ์‹œ์—์„œ ๋ฐฐ์—ด์€ ๋‘ ๊ฐœ์˜ ์›๊ณผ ํ•˜๋‚˜์˜ ์ •์‚ฌ๊ฐํ˜• ์ขŒํ‘œ๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค. ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด, ์›๋“ค์€ 50ํ”ฝ์…€ ์•„๋ž˜๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค. map()์œผ๋กœ ์ƒˆ ๋ฐ์ดํ„ฐ ๋ฐฐ์—ด์„ ์ƒ์„ฑํ•˜์—ฌ ์ด๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

import { useState } from 'react';

let initialShapes = [
  { id: 0, type: 'circle', x: 50, y: 100 },
  { id: 1, type: 'square', x: 150, y: 100 },
  { id: 2, type: 'circle', x: 250, y: 100 },
];

export default function ShapeEditor() {
  const [shapes, setShapes] = useState(
    initialShapes
  );

  function handleClick() {
    const nextShapes = shapes.map(shape => {
      if (shape.type === 'square') {
        // ๋ณ€๊ฒฝ์‹œํ‚ค์ง€ ์•Š๊ณ  ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
        return shape;
      } else {
        // 50px ์•„๋ž˜๋กœ ์ด๋™ํ•œ ์ƒˆ๋กœ์šด ์›์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
        return {
          ...shape,
          y: shape.y + 50,
        };
      }
    });
    // ์ƒˆ๋กœ์šด ๋ฐฐ์—ด๋กœ ๋ฆฌ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค.
    setShapes(nextShapes);
  }

  return (
    <>
      <button onClick={handleClick}>
        Move circles down!
      </button>
      {shapes.map(shape => (
        <div style={{
          background: 'purple',
          position: 'absolute',
          left: shape.x,
          top: shape.y,
          borderRadius:
            shape.type === 'circle'
              ? '50%' : '',
          width: 20,
          height: 20,
        }} />
      ))}
    </>
  );
}

๋ฐฐ์—ด ๋‚ด ํ•ญ๋ชฉ ๊ต์ฒดํ•˜๊ธฐ

๋ฐฐ์—ด์—์„œ ํ•˜๋‚˜ ์ด์ƒ์˜ ํ•ญ๋ชฉ์„ ๊ต์ฒดํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ํŠนํžˆ ํ”ํ•ฉ๋‹ˆ๋‹ค. arr[0] = 'bird'์™€ ๊ฐ™์€ ํ• ๋‹น์€ ์›๋ณธ ๋ฐฐ์—ด์„ ๋ณ€๊ฒฝ์‹œํ‚ค๋ฏ€๋กœ, ์ด ๊ฒฝ์šฐ์—๋„ map์„ ์‚ฌ์šฉํ•˜๋Š” ํŽธ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

ํ•ญ๋ชฉ์„ ๊ต์ฒดํ•˜๊ธฐ ์œ„ํ•ด map์„ ์ด์šฉํ•ด์„œ ์ƒˆ๋กœ์šด ๋ฐฐ์—ด์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค. map์„ ํ˜ธ์ถœํ•  ๋•Œ ๋‘ ๋ฒˆ์งธ ์ธ์ˆ˜๋กœ ํ•ญ๋ชฉ์˜ ์ธ๋ฑ์Šค๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ธ๋ฑ์Šค๋Š” ์›๋ž˜ ํ•ญ๋ชฉ(์ฒซ ๋ฒˆ์งธ ์ธ์ˆ˜)์„ ๋ฐ˜ํ™˜ํ• ์ง€ ๋‹ค๋ฅธ ํ•ญ๋ชฉ์„ ๋ฐ˜ํ™˜ํ• ์ง€๋ฅผ ๊ฒฐ์ •ํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

import { useState } from 'react';

let initialCounters = [
  0, 0, 0
];

export default function CounterList() {
  const [counters, setCounters] = useState(
    initialCounters
  );

  function handleIncrementClick(index) {
    const nextCounters = counters.map((c, i) => {
      if (i === index) {
        // ํด๋ฆญ๋œ counter๋ฅผ ์ฆ๊ฐ€์‹œํ‚ต๋‹ˆ๋‹ค.
        return c + 1;
      } else {
        // ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์€ ๋‚˜๋จธ์ง€๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
        return c;
      }
    });
    setCounters(nextCounters);
  }

  return (
    <ul>
      {counters.map((counter, i) => (
        <li key={i}>
          {counter}
          <button onClick={() => {
            handleIncrementClick(i);
          }}>+1</button>
        </li>
      ))}
    </ul>
  );
}

๋ฐฐ์—ด์— ํ•ญ๋ชฉ ์‚ฝ์ž…ํ•˜๊ธฐ

๊ฐ€๋”์€ ์‹œ์ž‘๋„, ๋๋„ ์•„๋‹Œ ์œ„์น˜์— ํ•ญ๋ชฉ์„ ์‚ฝ์ž…ํ•˜๊ณ  ์‹ถ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด, ... ๋ฐฐ์—ด ์ „๊ฐœ ๊ตฌ๋ฌธ๊ณผ slice() ํ•จ์ˆ˜๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. slice() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฐฐ์—ด์˜ โ€œ์ผ๋ถ€๋ถ„โ€์„ ์ž˜๋ผ๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•ญ๋ชฉ์„ ์‚ฝ์ž…ํ•˜๋ ค๋ฉด ์‚ฝ์ž… ์ง€์  ์•ž์— ์ž๋ฅธ ๋ฐฐ์—ด์„ ์ „๊ฐœํ•˜๊ณ , ์ƒˆ ํ•ญ๋ชฉ๊ณผ ์›๋ณธ ๋ฐฐ์—ด์˜ ๋‚˜๋จธ์ง€ ๋ถ€๋ถ„์„ ์ „๊ฐœํ•˜๋Š” ๋ฐฐ์—ด์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

์ด ์˜ˆ์‹œ์—์„œ ์‚ฝ์ž… ๋ฒ„ํŠผ์€ ํ•ญ์ƒ ์ธ๋ฑ์Šค 1์— ์‚ฝ์ž…๋ฉ๋‹ˆ๋‹ค.

import { useState } from 'react';

let nextId = 3;
const initialArtists = [
  { id: 0, name: 'Marta Colvin Andrade' },
  { id: 1, name: 'Lamidi Olonade Fakeye'},
  { id: 2, name: 'Louise Nevelson'},
];

export default function List() {
  const [name, setName] = useState('');
  const [artists, setArtists] = useState(
    initialArtists
  );

  function handleClick() {
    const insertAt = 1; // ๋ชจ๋“  ์ธ๋ฑ์Šค๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    const nextArtists = [
      // ์‚ฝ์ž… ์ง€์  ์ด์ „ ํ•ญ๋ชฉ
      ...artists.slice(0, insertAt),
      // ์ƒˆ ํ•ญ๋ชฉ
      { id: nextId++, name: name },
      // ์‚ฝ์ž… ์ง€์  ์ดํ›„ ํ•ญ๋ชฉ
      ...artists.slice(insertAt)
    ];
    setArtists(nextArtists);
    setName('');
  }

  return (
    <>
      <h1>Inspiring sculptors:</h1>
      <input
        value={name}
        onChange={e => setName(e.target.value)}
      />
      <button onClick={handleClick}>
        Insert
      </button>
      <ul>
        {artists.map(artist => (
          <li key={artist.id}>{artist.name}</li>
        ))}
      </ul>
    </>
  );
}

๋ฐฐ์—ด์— ๊ธฐํƒ€ ๋ณ€๊ฒฝ ์ ์šฉํ•˜๊ธฐ

์ „๊ฐœ ๊ตฌ๋ฌธ๊ณผ map(), filter() ๊ฐ™์€ ๋น„-๋ณ€๊ฒฝ ํ•จ์ˆ˜๋“ค๋กœ๋งŒ์œผ๋กœ๋Š” ํ•  ์ˆ˜ ์—†๋Š” ์ผ์ด ๋ช‡ ๊ฐ€์ง€ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋ฐฐ์—ด์„ ๋’ค์ง‘๊ฑฐ๋‚˜ ์ •๋ ฌํ•˜๊ณ  ์‹ถ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. JavaScript์˜ reverse() ๋ฐ sort() ํ•จ์ˆ˜๋Š” ์›๋ณธ ๋ฐฐ์—ด์„ ๋ณ€๊ฒฝ์‹œํ‚ค๋ฏ€๋กœ ์ง์ ‘ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

๋Œ€์‹ , ๋จผ์ € ๋ฐฐ์—ด์„ ๋ณต์‚ฌํ•œ ๋’ค ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด์„œ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

import { useState } from 'react';

const initialList = [
  { id: 0, title: 'Big Bellies' },
  { id: 1, title: 'Lunar Landscape' },
  { id: 2, title: 'Terracotta Army' },
];

export default function List() {
  const [list, setList] = useState(initialList);

  function handleClick() {
    const nextList = [...list];
    nextList.reverse();
    setList(nextList);
  }

  return (
    <>
      <button onClick={handleClick}>
        Reverse
      </button>
      <ul>
        {list.map(artwork => (
          <li key={artwork.id}>{artwork.title}</li>
        ))}
      </ul>
    </>
  );
}

์—ฌ๊ธฐ์„œ๋Š” ๋จผ์ € [...list] ์ „๊ฐœ ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•ด ์›๋ณธ ๋ฐฐ์—ด์˜ ๋ณต์‚ฌ๋ณธ์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์ด์ œ ๋ณต์‚ฌ๋ณธ์ด ์žˆ์œผ๋ฏ€๋กœ nextList.reverse() ๋˜๋Š” nextList.sort()์™€ ๊ฐ™์€ ๋ณ€๊ฒฝ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ nextList[0] = "something"๊ณผ ๊ฐ™์ด ๊ฐœ๋ณ„ ํ•ญ๋ชฉ์„ ํ• ๋‹นํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

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

const nextList = [...list];
nextList[0].seen = true; // ๋ฌธ์ œ: list[0]์„ ๋ณ€๊ฒฝ์‹œํ‚ต๋‹ˆ๋‹ค.
setList(nextList);

nextList์™€ list๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ๋ฐฐ์—ด์ด์ง€๋งŒ, nextList[0]๊ณผ list[0]์€ ๋™์ผํ•œ ๊ฐ์ฒด๋ฅผ ๊ฐ€๋ฆฌํ‚ต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ nextList[0].seen์„ ๋ณ€๊ฒฝํ•˜๋ฉด list[0].seen๋„ ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ state ๋ณ€๊ฒฝ์ด๋ฏ€๋กœ ํ”ผํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ค‘์ฒฉ๋œ JavaScript ๊ฐ์ฒด ์—…๋ฐ์ดํŠธ์™€ ์œ ์‚ฌํ•œ ๋ฐฉ์‹์œผ๋กœ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ณ€๊ฒฝํ•˜๋ ค๋Š” ๊ฐœ๋ณ„ ํ•ญ๋ชฉ์„ ๋ณ€๊ฒฝํ•˜๋Š” ๋Œ€์‹  ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๋ฐฐ์—ด ๋‚ด๋ถ€์˜ ๊ฐ์ฒด ์—…๋ฐ์ดํŠธํ•˜๊ธฐ

๊ฐ์ฒด๋Š” ์‹ค์ œ๋กœ ๋ฐฐ์—ด โ€œ๋‚ด๋ถ€โ€์— ์œ„์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ฝ”๋“œ์—์„œ โ€œ๋‚ด๋ถ€โ€๋กœ ๋‚˜ํƒ€๋‚ผ ์ˆ˜ ์žˆ์ง€๋งŒ ๋ฐฐ์—ด์˜ ๊ฐ ๊ฐ์ฒด๋Š” ๋ฐฐ์—ด์ด โ€œ๊ฐ€๋ฆฌํ‚ค๋Š”โ€ ๋ณ„๋„์˜ ๊ฐ’์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด list[0]์ฒ˜๋Ÿผ ์ค‘์ฒฉ๋œ ํ•„๋“œ๋ฅผ ๋ณ€๊ฒฝํ•  ๋•Œ ์ฃผ์˜ํ•ด์•ผ ํ•˜๋Š” ์ด์œ ์ž…๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์‚ฌ๋žŒ์˜ artwork ๋ชฉ๋ก์ด ๋ฐฐ์—ด์˜ ๋™์ผํ•œ ์š”์†Œ๋ฅผ ๊ฐ€๋ฆฌํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!

์ค‘์ฒฉ๋œ state๋ฅผ ์—…๋ฐ์ดํŠธํ•  ๋•Œ, ์—…๋ฐ์ดํŠธํ•˜๋ ค๋Š” ์ง€์ ๋ถ€ํ„ฐ ์ตœ์ƒ์œ„ ๋ ˆ๋ฒจ๊นŒ์ง€์˜ ๋ณต์‚ฌ๋ณธ์„ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ์‚ดํŽด๋ด…์‹œ๋‹ค.

์•„๋ž˜ ์˜ˆ์‹œ์—์„œ ๋‘ ๊ฐœ์˜ ๊ฐœ๋ณ„ artwork ๋ชฉ๋ก๋“ค์€ ์ดˆ๊ธฐ state๊ฐ€ ์„œ๋กœ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋‘ ๋ฆฌ์ŠคํŠธ๋Š” ๋ถ„๋ฆฌ๋˜์–ด์•ผ ํ•˜์ง€๋งŒ ๋ณ€๊ฒฝ์œผ๋กœ ์ธํ•ด ๋‘ ๋ชฉ๋ก์˜ state๊ฐ€ ์‹ค์ˆ˜๋กœ ๊ณต์œ ๋˜๊ณ  ํ•œ ๋ชฉ๋ก์˜ ์ฒดํฌ๋ฐ•์Šค๋ฅผ ์„ ํƒํ•˜๋ฉด ๋‹ค๋ฅธ ๋ชฉ๋ก์—๋„ ์˜ํ–ฅ์„ ๋ฏธ์นฉ๋‹ˆ๋‹ค.

import { useState } from 'react';

let nextId = 3;
const initialList = [
  { id: 0, title: 'Big Bellies', seen: false },
  { id: 1, title: 'Lunar Landscape', seen: false },
  { id: 2, title: 'Terracotta Army', seen: true },
];

export default function BucketList() {
  const [myList, setMyList] = useState(initialList);
  const [yourList, setYourList] = useState(
    initialList
  );

  function handleToggleMyList(artworkId, nextSeen) {
    const myNextList = [...myList];
    const artwork = myNextList.find(
      a => a.id === artworkId
    );
    artwork.seen = nextSeen;
    setMyList(myNextList);
  }

  function handleToggleYourList(artworkId, nextSeen) {
    const yourNextList = [...yourList];
    const artwork = yourNextList.find(
      a => a.id === artworkId
    );
    artwork.seen = nextSeen;
    setYourList(yourNextList);
  }

  return (
    <>
      <h1>Art Bucket List</h1>
      <h2>My list of art to see:</h2>
      <ItemList
        artworks={myList}
        onToggle={handleToggleMyList} />
      <h2>Your list of art to see:</h2>
      <ItemList
        artworks={yourList}
        onToggle={handleToggleYourList} />
    </>
  );
}

function ItemList({ artworks, onToggle }) {
  return (
    <ul>
      {artworks.map(artwork => (
        <li key={artwork.id}>
          <label>
            <input
              type="checkbox"
              checked={artwork.seen}
              onChange={e => {
                onToggle(
                  artwork.id,
                  e.target.checked
                );
              }}
            />
            {artwork.title}
          </label>
        </li>
      ))}
    </ul>
  );
}

๋ฌธ์ œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ์ฝ”๋“œ์— ์žˆ์Šต๋‹ˆ๋‹ค.

const myNextList = [...myList];
const artwork = myNextList.find(a => a.id === artworkId);
artwork.seen = nextSeen; // ๋ฌธ์ œ: ๊ธฐ์กด ํ•ญ๋ชฉ์„ ๋ณ€๊ฒฝ์‹œํ‚ต๋‹ˆ๋‹ค.
setMyList(myNextList);

myNextList ๋ฐฐ์—ด ์ž์ฒด๋Š” ์ƒˆ๋กœ์šด ๋ฐฐ์—ด์ด์ง€๋งŒ, ํ•ญ๋ชฉ ์ž์ฒด๋Š” myList ์›๋ณธ ๋ฐฐ์—ด๊ณผ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ artwork.seen์„ ๋ณ€๊ฒฝํ•˜๋ฉด ์›๋ณธ artwork ํ•ญ๋ชฉ์ด ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น artwork ํ•ญ๋ชฉ์€ yourArtWorks์—๋„ ์กด์žฌํ•˜๋ฏ€๋กœ ๋ฒ„๊ทธ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๋ฒ„๊ทธ๋Š” ์ƒ๊ฐํ•˜๊ธฐ ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์ง€๋งŒ ๋‹คํ–‰ํžˆ๋„ state ๋ณ€๊ฒฝ์„ ํ”ผํ•˜๋ฉด ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

map์„ ์‚ฌ์šฉํ•˜๋ฉด ์ด์ „ ํ•ญ๋ชฉ์˜ ๋ณ€๊ฒฝ ์—†์ด ์—…๋ฐ์ดํŠธ๋œ ๋ฒ„์ „์œผ๋กœ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

setMyList(myList.map(artwork => {
if (artwork.id === artworkId) {
// ๋ณ€๊ฒฝ๋œ *์ƒˆ* ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
return { ...artwork, seen: nextSeen };
} else {
// ๋ณ€๊ฒฝ์‹œํ‚ค์ง€ ์•Š๊ณ  ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
return artwork;
}
}));

์—ฌ๊ธฐ์„œ ...๋Š” ๊ฐ์ฒด์˜ ๋ณต์‚ฌ๋ณธ ์ƒ์„ฑ์— ์‚ฌ์šฉ๋˜๋Š” ๊ฐ์ฒด ์ „๊ฐœ ๊ตฌ๋ฌธ์ž…๋‹ˆ๋‹ค.

์ด ์ ‘๊ทผ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ฉด, ๊ธฐ์กด state ํ•ญ๋ชฉ์ด ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๊ณ , ๋ฒ„๊ทธ๊ฐ€ ์ˆ˜์ •๋ฉ๋‹ˆ๋‹ค.

import { useState } from 'react';

let nextId = 3;
const initialList = [
  { id: 0, title: 'Big Bellies', seen: false },
  { id: 1, title: 'Lunar Landscape', seen: false },
  { id: 2, title: 'Terracotta Army', seen: true },
];

export default function BucketList() {
  const [myList, setMyList] = useState(initialList);
  const [yourList, setYourList] = useState(
    initialList
  );

  function handleToggleMyList(artworkId, nextSeen) {
    setMyList(myList.map(artwork => {
      if (artwork.id === artworkId) {
        // ๋ณ€๊ฒฝ๋œ *์ƒˆ* ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
        return { ...artwork, seen: nextSeen };
      } else {
        // ๋ณ€๊ฒฝ์‹œํ‚ค์ง€ ์•Š๊ณ  ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
        return artwork;
      }
    }));
  }

  function handleToggleYourList(artworkId, nextSeen) {
    setYourList(yourList.map(artwork => {
      if (artwork.id === artworkId) {
        // ๋ณ€๊ฒฝ๋œ *์ƒˆ* ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
        return { ...artwork, seen: nextSeen };
      } else {
        // ๋ณ€๊ฒฝ์‹œํ‚ค์ง€ ์•Š๊ณ  ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
        return artwork;
      }
    }));
  }

  return (
    <>
      <h1>Art Bucket List</h1>
      <h2>My list of art to see:</h2>
      <ItemList
        artworks={myList}
        onToggle={handleToggleMyList} />
      <h2>Your list of art to see:</h2>
      <ItemList
        artworks={yourList}
        onToggle={handleToggleYourList} />
    </>
  );
}

function ItemList({ artworks, onToggle }) {
  return (
    <ul>
      {artworks.map(artwork => (
        <li key={artwork.id}>
          <label>
            <input
              type="checkbox"
              checked={artwork.seen}
              onChange={e => {
                onToggle(
                  artwork.id,
                  e.target.checked
                );
              }}
            />
            {artwork.title}
          </label>
        </li>
      ))}
    </ul>
  );
}

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

Immer๋กœ ๊ฐ„๊ฒฐํ•œ ์—…๋ฐ์ดํŠธ ๋กœ์ง ์ž‘์„ฑํ•˜๊ธฐ

๋ณ€๊ฒฝ ์—†์ด ์ค‘์ฒฉ๋œ ๋ฐฐ์—ด์„ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๊ฒƒ์€ ๊ฐ์ฒด์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์•ฝ๊ฐ„ ๋ฐ˜๋ณต์ ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์ผ๋ฐ˜์ ์œผ๋กœ ๊นŠ์€ ๋ ˆ๋ฒจ๊นŒ์ง€์˜ state๋ฅผ ์—…๋ฐ์ดํŠธํ•  ํ•„์š”๋Š” ์—†์Šต๋‹ˆ๋‹ค. state ๊ฐ์ฒด๊ฐ€ ๋งค์šฐ ๊นŠ๋‹ค๋ฉด ๋‹ค๋ฅด๊ฒŒ ์žฌ๊ตฌ์„ฑํ•˜์—ฌ ํ‰ํ‰ํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • state ๊ตฌ์กฐ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ณ  ์‹ถ์ง€ ์•Š๋‹ค๋ฉด, Immer ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์†์‰ฝ๊ฒŒ ๋ณ€๊ฒฝ ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•˜์—ฌ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ณ  ๋ณต์‚ฌ๋ณธ์„ ์ƒ์„ฑํ•˜์—ฌ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ์€ Immer๋กœ ๋‹ค์‹œ ์ž‘์„ฑํ•œ Art Bucket List ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค.

{
  "dependencies": {
    "immer": "1.7.3",
    "react": "latest",
    "react-dom": "latest",
    "react-scripts": "latest",
    "use-immer": "0.5.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },
  "devDependencies": {}
}

Immer๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด artwork.seen = nextSeen๊ณผ ๊ฐ™์ด ๋ณ€๊ฒฝํ•ด๋„ ๊ดœ์ฐฎ๋‹ค๋Š” ๊ฒƒ์— ์œ ์˜ํ•˜์„ธ์š”.

updateMyTodos(draft => {
const artwork = draft.find(a => a.id === artworkId);
artwork.seen = nextSeen;
});

์ด๋Š” ์›๋ณธ state๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, Immer์—์„œ ์ œ๊ณตํ•˜๋Š” ํŠน์ˆ˜ draft ๊ฐ์ฒด๋ฅผ ๋ณ€๊ฒฝํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ push()์™€ pop()๊ฐ™์€ ๋ณ€๊ฒฝ ํ•จ์ˆ˜๋“ค๋„ draft์˜ ์ปจํ…์ธ ์— ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚ด๋ถ€์ ์œผ๋กœ Immer๋Š” ํ•ญ์ƒ draft์—์„œ ์ˆ˜ํ–‰ํ•œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์— ๋”ฐ๋ผ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋‹ค์Œ state๋ฅผ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด state๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ ๋„ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋งค์šฐ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์š”์•ฝ

  • ๋ฐฐ์—ด์„ state๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ์ง€๋งŒ ๋ณ€๊ฒฝํ•˜๋ฉด ์•ˆ๋ฉ๋‹ˆ๋‹ค.
  • ๋ฐฐ์—ด์„ ๋ณ€๊ฒฝํ•˜๋Š” ๋Œ€์‹  ๋ฐฐ์—ด์˜ ์ƒˆ๋กœ์šด ๋ฒ„์ „์„ ๋งŒ๋“ค๊ณ , state๋ฅผ ์—…๋ฐ์ดํŠธ ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.
  • [...arr, newItem] ๋ฐฐ์—ด ์ „๊ฐœ ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ƒˆ ํ•ญ๋ชฉ์„ ํฌํ•จํ•œ ๋ฐฐ์—ด์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • filter()์™€ map()์„ ์‚ฌ์šฉํ•˜์—ฌ ํ•„ํ„ฐ๋ง๋œ ํ•ญ๋ชฉ๋“ค์ด๋‚˜ ๋ณ€ํ™˜๋œ ํ•ญ๋ชฉ๋“ค์„ ๊ฐ€์ง„ ๋ฐฐ์—ด์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • Immer๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋“œ ๊ฐ„๊ฒฐ์„ฑ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฑŒ๋ฆฐ์ง€ 1 of 4:
์žฅ๋ฐ”๊ตฌ๋‹ˆ์˜ ํ•ญ๋ชฉ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ

โ€+โ€ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ํ•ด๋‹น ์ˆซ์ž๊ฐ€ ์ฆ๊ฐ€ํ•˜๋„๋ก handleIncreaseClick ๋กœ์ง์„ ์ฑ„์›Œ๋ณด์„ธ์š”.

import { useState } from 'react';

const initialProducts = [{
  id: 0,
  name: 'Baklava',
  count: 1,
}, {
  id: 1,
  name: 'Cheese',
  count: 5,
}, {
  id: 2,
  name: 'Spaghetti',
  count: 2,
}];

export default function ShoppingCart() {
  const [
    products,
    setProducts
  ] = useState(initialProducts)

  function handleIncreaseClick(productId) {

  }

  return (
    <ul>
      {products.map(product => (
        <li key={product.id}>
          {product.name}
          {' '}
          (<b>{product.count}</b>)
          <button onClick={() => {
            handleIncreaseClick(product.id);
          }}>
            +
          </button>
        </li>
      ))}
    </ul>
  );
}