[React] key

๊ฒจ๋ ˆยท2024๋…„ 11์›” 20์ผ

[React] ๋ฆฌ์•กํŠธ ์Šคํ„ฐ๋””

๋ชฉ๋ก ๋ณด๊ธฐ
40/95

๐Ÿ“ key
๋ฆฌ์•กํŠธ์—์„œ key๋Š” ์ปดํฌ๋„ŒํŠธ ๋ฐฐ์—ด์„ ๋ Œ๋”๋งํ–ˆ์„ ๋•Œ ์–ด๋–ค ์›์†Œ์— ๋ณ€๋™์ด ์žˆ์—ˆ๋Š”์ง€ ์•Œ์•„๋‚ด๋ ค๊ณ  ์‚ฌ์šฉํ•œ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ์œ ๋™์ ์ธ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃฐ ๋•Œ๋Š” ์›์†Œ๋ฅผ ์ƒˆ๋กœ ์ƒ์„ฑํ•  ์ˆ˜๋„, ์ œ๊ฑฐํ•  ์ˆ˜๋„, ์ˆ˜์ •ํ•  ์ˆ˜๋„ ์žˆ๋Š”๋ฐ key๊ฐ€ ์—†์„ ๋•Œ๋Š” Virtual DOM์„ ๋น„๊ตํ•˜๋Š” ๊ณผ์ •์—์„œ ๋ฆฌ์ŠคํŠธ๋ฅผ ์ˆœ์ฐจ์ ์œผ๋กœ ๋น„๊ตํ•˜๋ฉด์„œ ๋ณ€ํ™”๋ฅผ ๊ฐ์ง€ํ•œ๋‹ค.

ํ•˜์ง€๋งŒ key๊ฐ€ ์žˆ๋‹ค๋ฉด?

์ด ๊ฐ’์„ ์‚ฌ์šฉํ•ด ์–ด๋–ค ๋ณ€ํ™”๊ฐ€ ์ผ์–ด๋‚ฌ๋Š”์ง€ ๋”์šฑ ๋น ๋ฅด๊ฒŒ ์•Œ์•„๋‚ผ ์ˆ˜ ์žˆ๋‹ค.


key ์„ค์ •

๐Ÿ‘‰ map ํ•จ์ˆ˜์˜ ์ธ์ž๋กœ ์ „๋‹ฌ๋˜๋Š” ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ ์ปดํฌ๋„ŒํŠธ props๋ฅผ ์„ค์ •ํ•˜๋“ฏ ์„ค์ •ํ•˜๋ฉด ๋จ.

โœ” key ๊ฐ’์€ ์–ธ์ œ๋‚˜ ์œ ์ผํ•ด์•ผ ํ•จ.
๋”ฐ๋ผ์„œ ๋ฐ์ดํ„ฐ๊ฐ€ ๊ฐ€์ง„ ๊ณ ์œณ๊ฐ’์„ key ๊ฐ’์œผ๋กœ ์„ค์ •ํ•ด์•ผ ํ•œ๋‹ค.


  • ์˜ˆ์ œ ์ปดํฌ๋„ŒํŠธ ์ˆ˜์ •
// IterationSample.jsx
const IterationSample = () => {
  const names = ['๋ˆˆ์‚ฌ๋žŒ', '์–ผ์Œ', '๋ˆˆ', '๋ฐ”๋žŒ'];
  const namesList = names.map((name, index) => <li key={index}>{name}</li>);
  return <ul>{namesList}</ul>;
};
 
export default IterationSample;

์ด์ œ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ์—์„œ ๋” ์ด์ƒ ๊ฒฝ๊ณ  ๋ฉ”์‹œ์ง€๋ฅผ ํ‘œ์‹œํ•˜์ง€ ์•Š์„ ๊ฒƒ!

๊ณ ์œ ํ•œ ๊ฐ’์ด ์—†์„ ๋•Œ๋งŒ index ๊ฐ’์„ key๋กœ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. index๋ฅผ key๋กœ ์‚ฌ์šฉํ•˜๋ฉด ๋ฐฐ์—ด์ด ๋ณ€๊ฒฝ๋  ๋•Œ ํšจ์œจ์ ์œผ๋กœ ๋ฆฌ๋ Œ๋”๋งํ•˜์ง€ ๋ชปํ•œ๋‹ค.


(+) ์‘์šฉํ•˜๊ธฐ

  • IterationSample ์ปดํฌ๋„ŒํŠธ์—์„œ useState๋ฅผ ์‚ฌ์šฉํ•ด ์ƒํƒœ๋ฅผ ์„ค์ •.

    • ์„ธ ๊ฐ€์ง€ ์ƒํƒœ๋ฅผ ์‚ฌ์šฉ
      • ๋ฐ์ดํ„ฐ ๋ฐฐ์—ด
      • ํ…์ŠคํŠธ๋ฅผ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ๋Š” input์˜ ์ƒํƒœ
      • ๋ฐ์ดํ„ฐ ๋ฐฐ์—ด์—์„œ ์ƒˆ๋กœ์šด ํ•ญ๋ชฉ์„ ์ถ”๊ฐ€ํ•  ๋•Œ ์‚ฌ์šฉํ•  ๊ณ ์œ  id๋ฅผ ์œ„ํ•œ ์ƒํƒœ

๋ฐฐ์—ด์„ ์„ค์ •ํ•  ๋•Œ, ์กฐ๊ธˆ ์ „์—๋Š” ๋‹จ์ˆœํžˆ ๋ฌธ์ž์—ด๋กœ ์ด๋ฃจ์–ด์ง„ ๋ฐฐ์—ด์„ ๋งŒ๋“ค์—ˆ์Œ.

์ด๋ฒˆ์—๋Š” ๊ฐ์ฒด ํ˜•ํƒœ๋กœ ์ด๋ฃจ์–ด์ง„ ๋ฐฐ์—ด์„ ๋งŒ๋“ค์–ด๋ณด์ž! ๊ทธ๋ฆฌ๊ณ  ํ•ด๋‹น ๊ฐ์ฒด์—๋Š” ๋ฌธ์ž์—ด๊ณผ ๊ณ ์œ  id ๊ฐ’์ด ์žˆ์Œ.

// IterationSample.jsx
// 6.4.1 ์‘์šฉํ•˜๊ธฐ
import React, { useState } from 'react';
const IterationSample = () => {
  const [names, setNames] = useState([
    { id: 1, text: '๋ˆˆ์‚ฌ๋žŒ' },
    { id: 2, text: '์–ผ์Œ' },
    { id: 3, text: '๋ˆˆ' },
    { id: 4, text: '๋ฐ”๋žŒ' },
  ]);
  const [inputText, setInputText] = useState('');
  const [nextId, setNextId] = useState(5); // ์ƒˆ๋กœ์šด ํ•ญ๋ชฉ์„ ์ถ”๊ฐ€ํ•  ๋•Œ ์‚ฌ์šฉํ•  id

  const namesList = names.map((name) => <li key={name.id}>{name.text}</li>);
  return <ul>{namesList}</ul>;
};

export default IterationSample;


(+) ์กฐ๊ธˆ ๋” ์‘์šฉํ•ด์„œ '์ถ”๊ฐ€' ๋ฒ„ํŠผ ๋งŒ๋“ค์–ด๋ณด๊ธฐ

// 6.4.2 ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ ๊ตฌํ˜„ํ•˜๊ธฐ
import React, { useState } from 'react';
const IterationSample = () => {
  const [names, setNames] = useState([
    { id: 1, text: '๋ˆˆ์‚ฌ๋žŒ' },
    { id: 2, text: '์–ผ์Œ' },
    { id: 3, text: '๋ˆˆ' },
    { id: 4, text: '๋ฐ”๋žŒ' },
  ]);
  const [inputText, setInputText] = useState('');
  const [nextId, setNextId] = useState(5);

  const onChange = (e) => setInputText(e.trarget.value);

  const nameList = names.map((name) => <li key={name.id}>{name.text}</li>);
  return (
    <>
      <input type={inputText} onChange={onChange} />
      <button>์ถ”๊ฐ€</button>
      <ul>{nameList}</ul>
    </>
  );
};
export default IterationSample;

๊ทธ๋‹ค์Œ์—๋Š” ๋ฒ„ํŠผ์„ ํด๋ฆญํ–ˆ์„ ๋•Œ ํ˜ธ์ถœํ•  onClick ํ•จ์ˆ˜๋ฅผ ์„ ์–ธํ•ด์„œ ๋ฒ„ํŠผ์˜ onClick ์ด๋ฒคํŠธ๋กœ ์„ค์ •ํ•ด๋ณด์ž.

onClick ํ•จ์ˆ˜์—์„œ๋Š” ๋ฐฐ์—ด์˜ ๋‚ด์žฅ ํ•จ์ˆ˜ concat์„ ์‚ฌ์šฉํ•ด ์ƒˆ๋กœ์šด ํ•ญ๋ชฉ์„ ์ถ”๊ฐ€ํ•œ ๋ฐฐ์—ด์„ ๋งŒ๋“ค๊ณ , setNames๋ฅผ ํ†ตํ•ด ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•ด ์ค€๋‹ค.

// 6.4.2 ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ ๊ตฌํ˜„ํ•˜๊ธฐ
// 6.4.2 ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ ๊ตฌํ˜„ํ•˜๊ธฐ
import React, { useState } from 'react';

const IterationSample = () => {
  const [names, setNames] = useState([
    { id: 1, text: '๋ˆˆ์‚ฌ๋žŒ' },
    { id: 2, text: '์–ผ์Œ' },
    { id: 3, text: '๋ˆˆ' },
    { id: 4, text: '๋ฐ”๋žŒ' },
  ]);
  const [inputText, setInputText] = useState('');
  const [nextId, setNextId] = useState(5); // ์ƒˆ๋กœ์šด ํ•ญ๋ชฉ์„ ์ถ”๊ฐ€ํ•  ๋•Œ ์‚ฌ์šฉํ•  id

  const onChange = (e) => setInputText(e.target.value);
  const onClick = () => {
    const nextNames = names.concat({
      id: nextId, // nextId ๊ฐ’์„ id๋กœ ์„ค์ •
      text: inputText,
    });
    setNextId(nextId + 1); // nextId ๊ฐ’์— 1์„ ๋” ํ•จ.
    setNames(nextNames); // names ๊ฐ’์„ ์—…๋ฐ์ดํŠธ
    setInputText(''); // inputText๋ฅผ ๋น„์›€.
  };

  const namesList = names.map((name) => <li key={name.id}>{name.text}</li>);
  return (
    <>
      <input value={inputText} onChange={onChange} />
      <button onClick={onClick}>์ถ”๊ฐ€</button>
      <ul>{namesList}</ul>
    </>
  );
};

export default IterationSample;

  • ๋ฐฐ์—ด์— ์ƒˆ ํ•ญ๋ชฉ์„ ์ถ”๊ฐ€ํ•  ๋•Œ ๋ฐฐ์—ด์˜ push ํ•จ์ˆ˜ ๋Œ€์‹  concat ์‚ฌ์šฉ!
    ๐Ÿ‘‰ push ํ•จ์ˆ˜๋Š” ๊ธฐ์กด ๋ฐฐ์—ด ์ž์ฒด๋ฅผ ๋ณ€๊ฒฝํ•ด ์คŒ
    ๐Ÿ‘‰ concat์€ ์ƒˆ๋กœ์šด ๋ฐฐ์—ด์„ ๋งŒ๋“ค์–ด ์ค€๋‹ค๋Š” ์ฐจ์ด์ ์ด ์žˆ์Œ.

๋ฆฌ์•กํŠธ์—์„œ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•  ๋•Œ๋Š” ๊ธฐ์กด ์ƒํƒœ๋ฅผ ๊ทธ๋Œ€๋กœ ๋‘๋ฉด์„œ ์ƒˆ๋กœ์šด ๊ฐ’์„ ์ƒํƒœ๋กœ ์„ค์ •ํ•ด์•ผ ํ•œ๋‹ค. ์ด๋ฅผ ๋ถˆ๋ณ€์„ฑ ์œ ์ง€๋ผ๊ณ  ํ•˜๋Š”๋ฐ, ๊ทธ๋ž˜์•ผ ๋‚˜์ค‘์— ๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ์˜ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.


(+) ๋”๋ธ” ํด๋ฆญํ•˜๋ฉด ์‚ญ์ œ๋˜๋Š” ๊ฒƒ๋„ ์ถ”๊ฐ€ํ•ด๋ณด์ž...

  // ๊ธฐ์กด ์ถ”๊ฐ€ ๊ธฐ๋Šฅ ๊ตฌํ˜„ ์ฃผ์„ ์ฒ˜๋ฆฌํ•˜๊ณ ...
  // const namesList = names.map((name) => <li key={name.id}>{name.text}</li>);
  //
  // ์ถ”๊ฐ€ + ์‚ญ์ œ ๊ธฐ๋Šฅ ๊ตฌํ˜„
  const onRemove = id => {
    const nextNames = names.filter(name => name.id !== id);
    setNames(nextNames);
  };
  const namesList = names.map(name => (
    <li key={name.id} onDoubleClick={() => onRemove(name.id)}>
      {name.text}
    </li>
  ));

profile
ํ˜ธ๋–ก ์‹ ๋ฌธ์ง€์—์„œ ๊ฐœ๋ฐœ์ž๋กœ ํ™˜์ƒ

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