๐จ 21. ๋ ๋ ํธ๋ฆฌ & key์ ์ง์ค: ์ปดํฌ๋ํธ ๋์ผ์ฑ์ ๋น๋ฐ
๐ ๊ฐ์
์ํ(State)๊ฐ ์ค์ ๋ก ์ด๋ ๋ณด๊ด๋๋์ง, key๊ฐ ๋จ์ ๋ชฉ๋ก ์๋ณ์๊ฐ ์๋ '์ปดํฌ๋ํธ ์ฃผ๋ฏผ๋ฑ๋ก๋ฒํธ'์ธ ์ด์ ๋ฅผ React ๋ ๋ ํธ๋ฆฌ ๊ตฌ์กฐ๋ก ํํค์นฉ๋๋ค.
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- ์ํ(State) ๊ฐ ์ปดํฌ๋ํธ ํจ์ "์"์ด ์๋๋ผ React ๊ฐ ๋ ๋ ํธ๋ฆฌ(Render Tree) ์ "์์น"์ ์ฐ๊ฒฐํด์ ๋ณด๊ดํ๋ค๋ ์ฌ์ค์ ์ค๋ช ํ ์ ์์ด์.
key๊ฐ ๋จ์ ๋ชฉ๋ก ์๋ณ์๊ฐ ์๋๋ผ, ์ปดํฌ๋ํธ์ "์ฃผ๋ฏผ๋ฑ๋ก๋ฒํธ"์ด์ ์ํ ์ฌํ์ ํตํญํ์์ ์ดํดํ ์ ์์ด์.keyํธ๋ฆญ์ ์ค๋ฌด์์ ํผ ๊ฐ์ ๋ฆฌ์ , ์ฑํ ์ฐฝ ์ธ์คํด์ค ๋ถ๋ฆฌ ๋ฑ์ ๋ฐ๋ก ์ ์ฉํ ์ ์์ด์.- ๋ ๋ ํจ์ ์์ ์ปดํฌ๋ํธ๋ฅผ ์ค์ฒฉ ์ ์ํ๋ฉด ์ ์ํ๊ฐ ๋งค๋ฒ ํญ๋ฐํ๋์ง ์ ํํ ์ง๋จํ ์ ์์ด์.
๐ ๋ชฉ์ฐจ
- ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
- ์ ์์์ผ ํ๋๊ฐ: ์ํ๊ฐ "์ฌ๋ผ์ง๋" ๋ฏธ์คํฐ๋ฆฌ
- ๋น์ ๋ก ๋จผ์ ์ดํดํ๊ธฐ
- ๋ ๋ ํธ๋ฆฌ๋ ๋ฌด์์ธ๊ฐ
- ์ํ๋ ํธ๋ฆฌ์ "์์น"์ ์ฐ๋ค
- ๊ฐ์ ์์น, ๊ฐ์ ํ์ โ ์ํ ๋ณด์กด์ ํจ์
- ๊ฐ์ ์์น, ๋ค๋ฅธ ํ์ โ ์ํ ์์ ๋ฆฌ์
- key โ ์ปดํฌ๋ํธ์ ์ฃผ๋ฏผ๋ฑ๋ก๋ฒํธ
- key ํธ๋ฆญ ์ค๋ฌด ํจํด
- ์ ๋ ๊ธ์ง: ๋ ๋ ํจ์ ์์ ์ปดํฌ๋ํธ ์ค์ฒฉ ์ ์
- ์๋ฌ ํด๊ฒฐ ์นดํ๋ก๊ทธ
- ์ด๋ฒ์ ๋ฐฐ์ด ๋ด์ฉ ์ด์ ๋ฆฌ
- ๋ง๋ฌด๋ฆฌ ํด์ฆ
- ๋ ์์๋ณด๊ธฐ
๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
โฑ๏ธ ์์ ์ฝ๊ธฐ ์๊ฐ: 15๋ถ (์ ์ฒด) / ํต์ฌ ํํธ๋ง: 8๋ถ
๐บ๏ธ ์ด ๋ฌธ์์ ํ๋ฆ
[๋ ๋ ํธ๋ฆฌ ๊ฐ๋ ] โ [์ํ ๋ณด๊ด ์์น์ ๋น๋ฐ] โ [ํ์ ๋น๊ต ์๋ฆฌ] โ [key์ ์ง์ง ์ ์ฒด] โ [์ค๋ฌด key ํธ๋ฆญ] โ [์น๋ช ์ ์ํฐ ํจํด] โ [์๋ฌ ์นดํ๋ก๊ทธ]
๐ฏ ์ด ๋ฌธ์๋ฅผ ๋ค ์ฝ์ผ๋ฉด ํ ์ ์๋ ๊ฒ
- "์ ์ปดํฌ๋ํธ๋ฅผ ๊ต์ฒดํ๋๋ฐ ์ํ๊ฐ ์ ์ง๋์ง?" ๋ฅผ ์ค์ค๋ก ์ค๋ช ํ ์ ์๋ค.
-
key๋ฅผ ๋ชฉ๋ก ์ด์ธ์ ๊ณณ์์๋ ์ ๋ต์ ์ผ๋ก ํ์ฉํ ์ ์๋ค. - ์ฝ๋ ๋ฆฌ๋ทฐ์์ "๋ ๋ ์์ ์ปดํฌ๋ํธ ์ ์ ๊ธ์ง!" ์ด์ ๋ฅผ ์ ํํ ์ค๋ช ํ ์ ์๋ค.
๐บ๏ธ ์ด ๋ฌธ์์ ๋ฐฐ๊ฒฝ ์ธ๊ณ๊ด: '์์๋ค ์ปค๋ฎค๋ํฐ'
- ์์ฒ (์ ์ ํ๋ก ํธ์๋): "์ํธ ๋, ์ด์ํด์. ํ๋ ์ด์ด A ์์ ํ๋ ์ด์ด B ๋ก ์ ํํด๋ ์ ์๊ฐ ๊ทธ๋๋ก ๋จ์์์ด์. ๋ถ๋ช ํ ๋ค๋ฅธ ์ฌ๋์ธ๋ฐ ์ํ๊ฐ ๋ฆฌ์ ์ด ์ ๋๋ค๊ณ ์. ์ ๊ฐ ๋ฒ๊ทธ๋ฅผ ๋ง๋ ๊ฒ ์๋๊ฐ์?"
- ์ํธ(๋ฆฌ๋ ํ๋ก ํธ์๋): "๋ฒ๊ทธ ์๋์์, ์์ฒ ๋. React ๊ฐ ์๋์ ์ผ๋ก ๊ทธ๋ ๊ฒ ๋์ํ ๊ฑฐ์์. ์ํ๋ ์ปดํฌ๋ํธ ํจ์ ์์ ์ฌ๋ ๊ฒ ์๋๋ผ, ๋ ๋ ํธ๋ฆฌ์ ์์น ์ ์ฌ๋ ๊ฑฐ๊ฑฐ๋ ์. ๊ทธ ๊ฐ๋
์ ์ดํดํ๋ฉด
key๊ฐ ์ '๋จ์ ์๋ณ์'๊ฐ ์๋ '์ํ ํญํ'์ธ์ง๋ ๋ฐ๋ก ๋ณด์ฌ์." - ์์(PM): "์ ๊น, ๊ทธ๋ผ ๋๊ธ ์์ฑ ํผ๋ ์ ์ถ ํ์ ์ ์ง์์ง๋ ๋ฌธ์ ๋ ๊ทธ๊ฑฐ ๋๋ฌธ์ธ๊ฐ์? ๋งค๋ฒ ์๋ก ๋ง์ดํธํด์ ๋ฆฌ์ ์ํค๋ ํธ๋ฆญ์ด ์๋ค๊ณ ๋ค์๋๋ฐ..."
- ์ํธ: "๋ค, ๋ฐ๋ก ๊ทธ
keyํธ๋ฆญ์ด์์. ์ค๋ ํ ๋ฐฉ์ ์ ๋ฆฌํด ๋๋ฆด๊ฒ์."
๐ค ์ ์์์ผ ํ๋๊ฐ: ์ํ๊ฐ "์ฌ๋ผ์ง๋" ๋ฏธ์คํฐ๋ฆฌ ๐ข
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- ์ ์ ๊ฐ๋ฐ์๊ฐ ๊ฒช๋ "์ํ๊ฐ ์ ์ง๋๋/์ฌ๋ผ์ง๋" ํ์์ด ์ ์ผ์ด๋๋์ง ๊ตฌ์กฐ์ ์ผ๋ก ์ดํดํ ์ ์์ด์.
- React ๊ฐ ์ปดํฌ๋ํธ๋ฅผ ์ด๋ป๊ฒ "์ธ์"ํ๋์ง ๋ด๋ถ ์๋ฆฌ๋ฅผ ์ ์ ์์ด์.
์์๋ค ์ปค๋ฎค๋ํฐ์๋ ์คํฐ๋ ์ธ์ ์ ์ํ ๊ธฐ๋ฅ์ด ์์ด์. ์์ฒ ์ด ์ด๋ ๊ฒ ๋ง๋ค์์ต๋๋ค.
// โ ์์ฒ ์ด์ ์ด๊ธฐ ๊ตฌํ โ ์ ์๊ฐ ์ ์ง๋๋ ๋ฒ๊ทธ๋ฅผ ํ๊ณ ์๋ค
function Scoreboard() {
const [isPlayerA, setIsPlayerA] = useState(true); // ํ์ฌ ํ๋ ์ด์ด ํ ๊ธ ์ํ
return (
<div>
{/* ๐จ ์์ฒ ์ด์ ์คํด: "isPlayerA๊ฐ ๋ฐ๋๋ฉด ์์ ํ ๋ค๋ฅธ ์ฌ๋์ด๋๊น ์ ์ ๋ฆฌ์
๋๊ฒ ์ง?" */}
{isPlayerA ? (
<Counter person="์ํธ" /> // isPlayerA=true ์ผ ๋ ๋ ๋๋ง
) : (
<Counter person="์์ฒ " /> // isPlayerA=false ์ผ ๋ ๋ ๋๋ง
)}
<button onClick={() => setIsPlayerA(!isPlayerA)}>
๋ค์ ํ๋ ์ด์ด
</button>
</div>
);
}
function Counter({ person }) {
const [score, setScore] = useState(0); // ๊ฐ์์ ์ ์
return (
<div>
<h1>{person} ์ ์ ์: {score}</h1>
<button onClick={() => setScore(score + 1)}>+1</button>
</div>
);
}์์ฒ ์ด๋ "๋ค์ ํ๋ ์ด์ด" ๋ฒํผ์ ๋๋ฅด๋ฉด ์ํธ์ ์ ์ 5์ ์ด ์ฌ๋ผ์ง๊ณ ์์ฒ ์ ์ ์๊ฐ 0์ ์์ ์์๋ ๊ฑฐ๋ผ ๊ธฐ๋ํ์ด์. ํ์ง๋ง ์ค์ ๋ก ๋๋ฌ๋ณด๋ฉด ์ ์๊ฐ ๊ทธ๋๋ก ๋จ์์์ต๋๋ค. ์ด๊ฒ ๋ฐ๋ก React ๋ ๋ ํธ๋ฆฌ ์ดํด๊ฐ ์์ผ๋ฉด ์ ๋ ์์ธกํ ์ ์๋ ๋์์ด์์.
์ ์ด๋ฐ ์ผ์ด ๋ฒ์ด์ง๊น์? ํต์ฌ์ ๋ฐ๋ก ์ด๊ฑฐ์์:
React ๋ ์ปดํฌ๋ํธ ์ธ์คํด์ค(instance) ๋ฅผ "JSX ๋ฅผ ๋ฐํํ ํจ์"๊ฐ ์๋๋ผ, "๋ ๋ ํธ๋ฆฌ์์์ ์์น(position)"๋ก ์๋ณํด์.
๊ฐ์ <div> ์ ์ฒซ ๋ฒ์งธ ์์ ์์น์ ๊ณ์ํด์ Counter ํ์
์ด ์๋ค๋ฉด, React ๋ "์ ์ด ์๋ฆฌ์ ์๋ Counter ๋ ๋์ผํ ๋
์์ด๊ตฌ๋" ๋ผ๊ณ ํ๋จํ๊ณ ์ํ๋ฅผ ์ ์ง์์ผ์. person prop ์ด "์ํธ" ์์ "์์ฒ " ๋ก ๋ฐ๋์ด๋์.
์ด ๋์์ ์ดํดํ์ง ๋ชปํ๋ฉด:
- ์์์น ๋ชปํ ์ํ ์ ์ง โ ์๋์ ์ผ๋ก ๋ฆฌ์ ํด์ผ ํ ์ํ๊ฐ ์ด์๋จ์์.
- ์์์น ๋ชปํ ์ํ ๋ฆฌ์ โ ๋ฉ์ฉกํ ์ ๋ ฅ ์ค์ธ ํผ์ด ๊ฐ์๊ธฐ ์ด๊ธฐํ๋ผ์.
- ์ค์ฒฉ ์ปดํฌ๋ํธ ์ ์ ์ง๋ขฐ โ ์ฑ๋ฅ ์ต์ ํํ๋ ค๋ค ์คํ๋ ค ๋ชจ๋ ์ํ๊ฐ ๋งค๋ฒ ํญ๋ฐํด์.
๐๏ธ ๋น์ ๋ก ๋จผ์ ์ดํดํ๊ธฐ
๐ง 5์ด์๊ฒ ์ค๋ช ํ๋ค๋ฉด?
์นดํ์ ํ ์ด๋ธ์ด 10๊ฐ ์์ด์. ์๋์ด ์์ผ๋ฉด ๊ทธ ํ ์ด๋ธ ๋ฒํธ๋ก ๊ธฐ์ตํ๋ ๊ฑฐ์์.
1๋ฒ ํ ์ด๋ธ์ ํ ๋จธ๋๊ฐ ์์๋ค๊ฐ ๋๊ฐ๊ณ , ๊ทธ ์๋ฆฌ์ ํ ์๋ฒ์ง๊ฐ ์์ผ๋ฉด
์นดํ ์๋ฐ์(React) ์ "1๋ฒ ํ ์ด๋ธ ์๋" ์ด๋ผ๊ณ ๋ง ๊ธฐ์ตํด์.
๊ทธ๋์ ํ ๋จธ๋๊ฐ ์ํจ ์๋ฉ๋ฆฌ์นด๋ ธ(์ํ) ๊ฐ ์์ง๋ 1๋ฒ ํ ์ด๋ธ ์์ ์๋ ๊ฒ์ฒ๋ผ ์ทจ๊ธํด์.ํ์ง๋ง ํ ์ด๋ธ ๋ฒํธํ(key) ์ ๊ฐ์๋ผ์ฐ๋ฉด? "์, ์ด๊ฑด ์์ ํ ๋ค๋ฅธ ํ ์ด๋ธ์ด๋ค!" ํ๊ณ
์ฒ์๋ถํฐ ์๋ก ์ฃผ๋ฌธ์ ๋ฐ์์. ์๋ฉ๋ฆฌ์นด๋ ธ(์ํ) ๋ ์ฌ๋ผ์ง๊ณ ๊นจ๋ํ ํ ์ด๋ธ์ด ๋๋ ๊ฑฐ์ฃ .
React ์ ๋ ๋ ํธ๋ฆฌ๋ ์๋ฆฌ ๋ฐฐ์น๋(์ข์ํ) ์์. ์ํ๋ ๊ทธ ์๋ฆฌ์ ๋ถ์ด์๋ ํฌ์คํธ์์ด๊ณ ์. ์๋ฆฌ์ ์๋ ์ฌ๋(์ปดํฌ๋ํธ ์ธ์คํด์ค) ์ด ๋ฐ๋์ด๋, ๊ฐ์ ์๋ฆฌ์ ๊ฐ์ ์ข ๋ฅ์ ์์(์ปดํฌ๋ํธ ํ์ ) ๊ฐ ์์ผ๋ฉด ํฌ์คํธ์์ ๊ทธ๋๋ก์์.
key ๋ ์๋ฆฌ ๋ฒํธํ์ ์์ ์ ๋ฒํธํ์ผ๋ก ๊ต์ฒดํ๋ ๊ฑฐ์์. ๋ฒํธ๊ฐ ๋ฐ๋๋ฉด React ๋ ์์ ํ ์ ์๋ฆฌ๋ก ์ธ์ํด์, ๊ธฐ์กด ํฌ์คํธ์(์ํ) ์ ๋ชจ๋ ๋ผ์ด๋ด๊ณ ์ ํฌ์คํธ์(์ด๊ธฐ ์ํ) ์ ๋ถ์ฌ์.
๐งฉ ๋ ๋ ํธ๋ฆฌ๋ ๋ฌด์์ธ๊ฐ ๐ข
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- React ๊ฐ JSX ๋ฅผ ์ด๋ป๊ฒ ํธ๋ฆฌ ๊ตฌ์กฐ๋ก ํด์ํ๋์ง ์ค๋ช ํ ์ ์์ด์.
- "๋ ๋ ํธ๋ฆฌ์์์ ์์น" ๊ฐ ๋ฌด์์ ์๋ฏธํ๋์ง ์ ์ ์์ด์.
๐ค ์ ๊น, ๋จผ์ ์๊ฐํด๋ด
<div>์์<Counter />๊ฐ ํ๋ ์์ด์. ์กฐ๊ฑด๋ถ ๋ ๋๋ง์ผ๋ก ์ด<Counter />๋ฅผ ์จ๊ฒผ๋ค๊ฐ ๋ค์ ๋ณด์ฌ์ฃผ๋ฉด, ์ํ๋ ์ด์์์๊น์ ์๋๋ฉด ๋ฆฌ์ ๋ ๊น์?
์ง๊ฐ์ ์ผ๋ก ์์ธกํด๋ณด๊ณ ์๋๋ก ๋ด๋ ค๊ฐ์.
React ๊ฐ ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํ ๋, ๋ด๋ถ์ ์ผ๋ก UI ํธ๋ฆฌ(UI Tree) ๋ฅผ ๊ตฌ์ฑํด์. ์ด ํธ๋ฆฌ๋ ์ฐ๋ฆฌ๊ฐ ์์ฑํ JSX ๊ตฌ์กฐ๋ฅผ ๊ทธ๋๋ก ๋ฐ์ํด์.
// ์๋ JSX๋...
<App>
<Scoreboard>
<Counter person="์ํธ" />
</Scoreboard>
</App>
// React ๋ด๋ถ์์๋ ์ด๋ฐ ํธ๋ฆฌ๋ก ๊ด๋ฆฌ๋ผ์:
App
โโโ Scoreboard
โโโ Counter (position: 0๋ฒ์งธ ์์)
์ค์ํ ๊ฑด React ๊ฐ ๊ฐ ์ปดํฌ๋ํธ๋ฅผ ํธ๋ฆฌ์์์ ์์น๋ก ์๋ณํ๋ค๋ ๊ฑฐ์์. "Counter ๋ผ๋ ์ด๋ฆ์ ํจ์" ๊ฐ ์๋๋ผ, "Scoreboard ์ 0๋ฒ์งธ ์์" ์ด๋ผ๋ ์ขํ๋ก์.
// โ
๋ ๋ ํธ๋ฆฌ ์์น ๊ฐ๋
์ ์ฝ๋๋ก ํํํ๋ฉด
function Scoreboard() {
return (
<div>
{/* ์์น 0: ์ฌ๊ธฐ์ ๋ฌด์์ด ์ค๋ React๋ "div์ ์ฒซ ๋ฒ์งธ ์์"์ผ๋ก ์ธ์ */}
<Counter person="์ํธ" />
{/* ์์น 1: div์ ๋ ๋ฒ์งธ ์์ */}
<Counter person="์์ฒ " />
</div>
);
}์ ์์์์ ๋ <Counter /> ๋ ๋ค๋ฅธ ์์น์ ์์ด์. ๊ทธ๋์ React ๋ ์ด ๋์ ์์ ํ ๋ณ๊ฐ์ ์ธ์คํด์ค๋ก ๊ด๋ฆฌํ๊ณ , ๊ฐ์ ๋
๋ฆฝ์ ์ธ ์ํ๋ฅผ ๊ฐ์ ธ์.
๐ ์ฐ๊ฒฐ ๊ณ ๋ฆฌ
React ์ ์ฌ์กฐ์ (Reconciliation) ์๊ณ ๋ฆฌ์ฆ์ด ๋ฐ๋ก ์ด ํธ๋ฆฌ ๋น๊ต๋ฅผ ๋ด๋นํด์. "๊ฐ์ ์์น์ ๊ฐ์ ํ์ ์ด๋ฉด ์ ๋ฐ์ดํธ, ๋ค๋ฅธ ํ์ ์ด๋ฉด ์ ๊ฑฐ ํ ์๋ก ๋ง์ดํธ" ๋ผ๋ ๋จ์ํ ๊ท์น์ด ๋ชจ๋ ๊ฑธ ์ง๋ฐฐํด์.
๐ก ํ ์ค๋ก ๊ธฐ์ตํ๊ธฐ
React ์๊ฒ ์ปดํฌ๋ํธ์ ์ ์ฒด์ฑ์ "์ด๋ฆ"์ด ์๋๋ผ "๋ ๋ ํธ๋ฆฌ์์์ ์ข์ ๋ฒํธ"์์.
๐งฉ ์ํ๋ ํธ๋ฆฌ์ "์์น"์ ์ฐ๋ค ๐ข
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- ์ํ๊ฐ ์ปดํฌ๋ํธ ํจ์ ์์ด ์๋๋ผ React ๊ฐ ๊ด๋ฆฌํ๋ ์ธ๋ถ ์ ์ฅ์์ ์๋ค๋ ์ฌ์ค์ ์ ์ ์์ด์.
- ๊ฐ์ ์ปดํฌ๋ํธ๋ฅผ ๋ ๋ฒ ๋ ๋๋งํ๋ฉด ์ํ๊ฐ ๋ ๋ฆฝ์ ์ธ ์ด์ ๋ฅผ ์ค๋ช ํ ์ ์์ด์.
๐ค ์ ๊น, ๋จผ์ ์๊ฐํด๋ด
const [count, setCount] = useState(0)์ด ์ฝ๋์์count๋ ์ด๋์ ์ ์ฅ๋ ๊น์?
"์ปดํฌ๋ํธ ํจ์ ๋ด๋ถ์ ์ ์ฅ๋๊ฒ ์ง" ๊ฐ ์ง๊ฐ์ด๋ผ๋ฉด, ์๋๋ฅผ ์ฝ์ผ๋ฉด ์ธ๊ณ๊ด์ด ๋ค์งํ ๊ฑฐ์์.
React ์ ํต์ฌ ์ค๊ณ ์์น ์ค ํ๋๋, ์ํ๋ ์ปดํฌ๋ํธ ํจ์ ๋ฐ๊นฅ์ ์๋ค ๋ ๊ฑฐ์์. ์ข ๋ ์ ํํ ๋งํ๋ฉด, React ๋ฐํ์์ด ๊ด๋ฆฌํ๋ ๋ณ๋์ ์ ์ฅ์(fiber tree ๋๋ ๋ด๋ถ ๋ฉ๋ชจ๋ฆฌ) ์ ํธ๋ฆฌ ์์น๋ณ๋ก ์ฐ๊ฒฐ๋์ด ๋ณด๊ด๋ผ์.
// ๐งช ์คํ: ๊ฐ์ ์ปดํฌ๋ํธ๋ฅผ ๋ ๊ณณ์์ ๋ ๋๋ง
function App() {
return (
<div>
{/* ์ด ๋์ ๊ฐ์ Counter ํจ์๋ฅผ ์ฌ์ฉํ์ง๋ง... */}
<Counter /> {/* ์์น A: ์์ฒด์ ์ธ ์ํ๋ฅผ ๊ฐ์ง */}
<Counter /> {/* ์์น B: ์์น A์ ์์ ํ ๋
๋ฆฝ์ ์ธ ์ํ๋ฅผ ๊ฐ์ง */}
</div>
);
}์ ์ฝ๋์์ ์ผ์ชฝ Counter ๋ฅผ 5๋ฒ ํด๋ฆญํ๋ฉด ์ค๋ฅธ์ชฝ Counter ์๋ ์ํฅ์ด ์์ด์. ์ด๊ฑด ๋น์ฐํด ๋ณด์ด์ง๋ง, ๊ทธ ์ด์ ๋ฅผ ์ ๋๋ก ์ดํดํ๋ ์ฌ๋์ ๋๋ฌผ์ด์.
์ํ๊ฐ ํจ์ ์์ ์๋ค๋ฉด Counter ํจ์๋ ๋ ๋ฒ ํธ์ถ๋ ๋๋ง๋ค ๊ฐ์ ์ด๊ธฐ๊ฐ 0 ์์ ์์ํ ํ
๊ณ , ํด๋ฆญํด๋ React ๋ "์ด๋ค Counter ์ ์ํ๋ฅผ ๋ฐ๊ฟ์ผ ํ์ง?" ๋ฅผ ๋ชจ๋ฅผ ๊ฑฐ์์. ํ์ง๋ง ์ค์ ๋ก๋ React ๊ฐ ํธ๋ฆฌ ์์น A, B ๊ฐ๊ฐ์ ์ํ ์ฌ๋กฏ์ ๋ฐ๋ก ๋ง๋ค์ด ๊ด๋ฆฌํ๊ธฐ ๋๋ฌธ์ ๋
๋ฆฝ์ ์ผ๋ก ์๋ํด์.
// ๐ก React ๋ด๋ถ ๊ฐ๋
(์์ฌ ์ฝ๋, ์ค์ ๊ตฌํ๊ณผ ๋ค๋ฅผ ์ ์์)
const internalStateStore = {
"App > div > Counter[0]": { count: 5 }, // ์์น A
"App > div > Counter[1]": { count: 0 }, // ์์น B
};
// Counter ํจ์๊ฐ ์คํ๋ ๋ useState(0)๋
// ํ์ฌ ๋ ๋๋ง ์ค์ธ ์์น์ ์ฌ๋กฏ์ ์ฐพ์์ ๊ฐ์ ๋ฐํํด์์ด ์ฌ์ค์์ ์ค์ํ ๊ฒฐ๋ก ์ด ๋์์:
์ปดํฌ๋ํธ๊ฐ unmount(ํธ๋ฆฌ์์ ์ ๊ฑฐ) ๋๋ฉด, ๊ทธ ์์น์ ์ฐ๊ฒฐ๋ ์ํ ์ฌ๋กฏ๋ ํจ๊ป ์ญ์ ๋ผ์.
// ๐งช ์กฐ๊ฑด๋ถ ๋ ๋๋ง ์คํ
function App() {
const [showCounter, setShowCounter] = useState(true);
return (
<div>
{showCounter && <Counter />} {/* true๋ฉด ๋ง์ดํธ, false๋ฉด ์ธ๋ง์ดํธ */}
<button onClick={() => setShowCounter(!showCounter)}>
ํ ๊ธ
</button>
</div>
);
}showCounter ๋ฅผ false ๋ก ๋ฐ๊พธ๋ฉด Counter ๊ฐ ํธ๋ฆฌ์์ ์ ๊ฑฐ๋ผ์. ์ํ๋ ํจ๊ป ๋ ์๊ฐ์. ๋ค์ true ๋ก ๋ฐ๊พธ๋ฉด Counter ๊ฐ ์๋ก ๋ง์ดํธ๋๊ณ ์ํ๋ ์ด๊ธฐ๊ฐ 0 ์์ ๋ค์ ์์ํด์.
๐ก ํ ์ค๋ก ๊ธฐ์ตํ๊ธฐ
์ํ๋ ํจ์ ์์ด ์๋๋ผ React ๊ฐ ๋ง๋ ์ธ๋ถ ์ฃผ์๋ก์ ์๊ณ , ์ฃผ์๋ ํธ๋ฆฌ ์์น์์. ์ฃผ์๊ฐ ์ฌ๋ผ์ง๋ฉด(์ธ๋ง์ดํธ) ์ํ๋ ์ฌ๋ผ์ ธ์.
๐งฉ ๊ฐ์ ์์น, ๊ฐ์ ํ์ โ ์ํ ๋ณด์กด์ ํจ์ ๐ก
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- "๊ฐ์ ์์น์ ๊ฐ์ ์ปดํฌ๋ํธ ํ์ " ์ด๋ผ๋ฉด prop ์ด ๋ฌ๋ผ์ ธ๋ ์ํ๊ฐ ์ ์ง๋๋ ์ด์ ๋ฅผ ์ค๋ช ํ ์ ์์ด์.
- ์์ฒ ์ด์ ์ ์ํ ๋ฒ๊ทธ๊ฐ ์ ๋ฐ์ํ๋์ง ์ ํํ ์ง๋จํ ์ ์์ด์.
๐ค ์ ๊น, ๋จผ์ ์๊ฐํด๋ด
isPlayerA๊ฐ true ์ผ ๋<Counter person="์ํธ" />๋ฅผ, false ์ผ ๋<Counter person="์์ฒ " />๋ฅผ ๋ ๋๋งํด์.
"person" prop ์ด ๋ค๋ฅด๋๊น React ๊ฐ ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ก ์ธ์ํ ๊น์?
React ์ ์ฌ์กฐ์ (Reconciliation, '์ฌ์กฐ์ ' ์ ์ด์ ๊ฐ์ DOM ํธ๋ฆฌ์ ์ ๊ฐ์ DOM ํธ๋ฆฌ๋ฅผ ๋น๊ตํด์ ์ต์ํ์ ๋ณ๊ฒฝ๋ง ์ค์ DOM ์ ๋ฐ์ํ๋ ๊ณผ์ ์ด์์) ๊ท์น์ ๋จ์ํด์:
๊ฐ์ ์์น์ ๊ฐ์ ํ์ ์ด๋ฉด โ ๊ธฐ์กด ์ธ์คํด์ค๋ฅผ ์ฌ์ฌ์ฉํ๊ณ prop ๋ง ์ ๋ฐ์ดํธํด์.
// ๐ด ์ํ ๋ณด์กด ํจ์ โ ์์ฒ ์ด์ Scoreboard ๋ฒ๊ทธ ์ฌํ
function Scoreboard() {
const [isPlayerA, setIsPlayerA] = useState(true);
return (
<div>
{/*
React ๊ฐ ๋ณด๋ ๊ฒ: "div์ ์ฒซ ๋ฒ์งธ ์์ = Counter ํ์
"
isPlayerA๊ฐ true โ false ๋ก ๋ฐ๋ ๋:
- ์ด์ ํธ๋ฆฌ: Counter (person="์ํธ")
- ์ ํธ๋ฆฌ: Counter (person="์์ฒ ")
- React ํ๋จ: "๊ฐ์ ์์น, ๊ฐ์ ํ์
(Counter) โ ๊ธฐ์กด ์ธ์คํด์ค ์ ์ง, prop๋ง ๋ณ๊ฒฝ!"
- ๊ฒฐ๊ณผ: score ์ํ๊ฐ ์ ์ง๋จ ๐ฑ
*/}
{isPlayerA ? <Counter person="์ํธ" /> : <Counter person="์์ฒ " />}
<button onClick={() => setIsPlayerA(!isPlayerA)}>๋ค์ ํ๋ ์ด์ด</button>
</div>
);
}React ๋ prop ์ ๋ด์ฉ์ด ๋ฌ๋ผ๋ ํ์
์ด ๊ฐ์ผ๋ฉด "๊ฐ์ ์ปดํฌ๋ํธ์ ์ํ๋ฅผ ์
๋ฐ์ดํธํ๋ฉด ๋๋ค" ๊ณ ํ๋จํด์. person ์ด "์ํธ" ์์ "์์ฒ " ๋ก ๋ฐ๋์ด๋, ๊ทธ๊ฑด ๊ทธ๋ฅ ์
๋ฐ์ดํธ์ผ ๋ฟ์ด์์.
์ด๊ฒ ๋ฒ๊ทธ์ฒ๋ผ ๋ณด์ด์ง๋ง, ์ฌ์ค์ React ์ ์๋๋ ์ฑ๋ฅ ์ต์ ํ ์์. ์๋ฅผ ๋ค์ด ํญ ์ ํ UI ์์ ํญ ๋ด์ฉ์ด ๋ฐ๋์ด๋ ์คํฌ๋กค ์์น๋ ์ ๋ ฅ๊ฐ์ด ์ ์ง๋๊ธฐ๋ฅผ ์ํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ผ๋๊น์. ๋ฌธ์ ๋ ์์ฒ ์ด์ฒ๋ผ ์ด ๋์์ ๋ชจ๋ฅด๊ณ ๋ฆฌ์ ์ ๊ธฐ๋ํ ๋ ์์.
์๋ ๋ค์ด์ด๊ทธ๋จ์ผ๋ก ์ ๋ฆฌํด ๋ณผ๊ฒ์:
isPlayerA = true ์ผ ๋์ ํธ๋ฆฌ:
Scoreboard
โโโ div
โโโ Counter (score=5, person="์ํธ") โ ์์น 0
isPlayerA = false ๋ก ๋ณ๊ฒฝ ํ ํธ๋ฆฌ:
Scoreboard
โโโ div
โโโ Counter (score=5, person="์์ฒ ") โ ์์น 0, ๊ฐ์ ์์น!
// score ์ํ๊ฐ ๊ทธ๋๋ก ๋จ์์๋ค
โก๏ธ ๋ค์ ์น์ ์์๋: ๋ค๋ฅธ ํ์ ์ ๊ฐ์ ์์น์ ๋์ผ๋ฉด ์ํ๊ฐ ์ด๋ป๊ฒ ๋๋์ง ๋ณผ๊ฒ์. ๊ทธ ์๋ฆฌ๋ฅผ ์ดํดํ๋ฉด
key๊ฐ ์ "์ํ ํญํ" ์ธ์ง๊ฐ ๋ฐ๋ก ์ฐ๊ฒฐ๋ผ์.
๐ก ํ ์ค๋ก ๊ธฐ์ตํ๊ธฐ
React ๋ prop ๋ด์ฉ์ด ์๋๋ผ ์ปดํฌ๋ํธ ํ์ ๊ณผ ํธ๋ฆฌ ์์น ๋ฅผ ๋ณด๊ณ "๊ฐ์ ๋์ด๋ ๋ค๋ฅธ ๋์ด๋" ๋ฅผ ํ๋จํด์. ํ์ ๊ณผ ์์น๊ฐ ๊ฐ์ผ๋ฉด ์ํ๋ ์ ๋ ๋ฆฌ์ ๋์ง ์์์.
๐งฉ ๊ฐ์ ์์น, ๋ค๋ฅธ ํ์ โ ์ํ ์์ ๋ฆฌ์ ๐ก
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- ์ปดํฌ๋ํธ ํ์ ์ด ๋ฌ๋ผ์ง๋ฉด React ๊ฐ ๊ธฐ์กด ํธ๋ฆฌ๋ฅผ ์์ ํ ์ ๊ฑฐํ๊ณ ์๋ก ๋ง์ดํธํ๋ค๋ ๊ฑธ ์ ์ ์์ด์.
- ์ด ์๋ฆฌ๋ฅผ ์ญ์ด์ฉํด์ ์ํ๋ฅผ ๊ฐ์ ๋ก ๋ฆฌ์ ํ๋ ๋ฐฉ๋ฒ์ ์ ์ ์์ด์.
๊ฐ์ ์์น์ ๋ค๋ฅธ ํ์ ์ ์ปดํฌ๋ํธ๊ฐ ์ค๋ฉด, React ๋ ๊ธฐ์กด ์ปดํฌ๋ํธ๋ฅผ ์์ ํ ์ธ๋ง์ดํธํ๊ณ ์ ์ปดํฌ๋ํธ๋ฅผ ์ฒ์๋ถํฐ ๋ง์ดํธํด์. ๋น์ฐํ ์ํ๋ ์ด๊ธฐํ๋ผ์.
// โ
๋ค๋ฅธ ํ์
์ผ๋ก ๊ต์ฒด โ ์ํ ์์ ๋ฆฌ์
ํ์ธ
function App() {
const [showCounter, setShowCounter] = useState(true);
return (
<div>
{showCounter ? (
<Counter /> // Counter ํ์
โ ์์น 0
) : (
<p>์ ์๋ฅผ ๋ณด๋ ค๋ฉด ๋ฒํผ์ ๋๋ฅด์ธ์</p> // p ํ์
โ ์์น 0
)}
{/*
Counter โ p ๋ก ๋ฐ๋ ๋:
React ํ๋จ: "์์น 0์ ํ์
์ด Counter์์ p๋ก ๋ฌ๋ผ์ก๋ค!"
๋์: Counter ์ธ๋ง์ดํธ (์ํ ํ๊ดด) โ p ์๋ก ๋ง์ดํธ
*/}
<button onClick={() => setShowCounter(!showCounter)}>ํ ๊ธ</button>
</div>
);
}์ด ๋์์ ์ด์ฉํด์ ์๋์ ์ผ๋ก ์ํ๋ฅผ ๋ฆฌ์ ํ๋ ค๋ ์๋๋ ๋ณผ ์ ์์ด์:
// โ ์์งํ ํด๊ฒฐ์ฑ
โ ๋ค๋ฅธ ์ปดํฌ๋ํธ ๋ํผ๋ก ๊ฐ์ธ๊ธฐ
// (์ด ๋ฐฉ๋ฒ์ ๋์ํ์ง๋ง ์ฝ๋๊ฐ ์ง์ ๋ถํด์)
function Scoreboard() {
const [isPlayerA, setIsPlayerA] = useState(true);
return (
<div>
{isPlayerA ? (
<div> {/* div๋ก ๊ฐ์ธ์ "๋ค๋ฅธ ํ์
"์ฒ๋ผ ๋ณด์ด๊ฒ ํ๋ ค๋ ์๋ */}
<Counter person="์ํธ" />
</div>
) : (
<section> {/* section์ div์ ๋ค๋ฅธ ํ์
*/}
<Counter person="์์ฒ " />
</section>
)}
{/*
div์ section์ ๋ค๋ฅธ ํ์
์ด๋๊น ํ์
๋ถ์ผ์น โ ์ํ ๋ฆฌ์
ํ์ง๋ง ์ด๊ฑด ์๋ฏธ ์๋ ๋ํผ๋ฅผ ์ถ๊ฐํ๋ ์ง์ ๋ถํ ๋ฐฉ์์ด์์
*/}
</div>
);
}์ ๋ฐฉ์์ ์๋ํ๊ธด ํ์ง๋ง ์ฌ๋ฐ๋ฅธ ํด๊ฒฐ์ฑ
์ด ์๋์์. ์ฌ๋ฐ๋ฅธ ํด๊ฒฐ์ฑ
์ key ์์. ์ด์ด์ ๋ฐ๋ก ์์๋ด์.
๐ก ํ ์ค๋ก ๊ธฐ์ตํ๊ธฐ
ํ์ ์ด ๋ฌ๋ผ์ง๋ฉด React ๋ ๋ฌป์ง๋ ๋ฐ์ง์ง๋ ์๊ณ ๊ธฐ์กด ํธ๋ฆฌ๋ฅผ ํต์งธ๋ก ๋ฒ๋ฆฌ๊ณ ์๋ก ๋ง๋ค์ด์. ์ํ๋ ํจ๊ป ์ฆ๋ฐํด์.
๐งฉ key โ ์ปดํฌ๋ํธ์ ์ฃผ๋ฏผ๋ฑ๋ก๋ฒํธ ๐ก
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
key๊ฐ ๋ชฉ๋ก ๋ ๋๋ง๋ฟ ์๋๋ผ ์ปดํฌ๋ํธ ์ธ์คํด์ค์ "์ ์ ์ฆ๋ช ์" ์ญํ ์ ํ๋ค๋ ๊ฑธ ์ ์ ์์ด์.key๊ฐ ๋ฐ๋๋ฉด React ๊ฐ ์์ ํ ์ ์ปดํฌ๋ํธ๋ฅผ ๋ง์ดํธํ๋ค๋ ๊ฑธ ์ดํดํ ์ ์์ด์.
๐ค ์ ๊น, ๋จผ์ ์๊ฐํด๋ด
๋ฆฌ์คํธ๋ฅผ ๋ ๋๋งํ ๋key๊ฐ ์์ผ๋ฉด ๊ฒฝ๊ณ ๊ฐ ๋์ค๋๋ฐ, ๊ทธ ์ด์ ๊ฐ ๋จ์ํ "React ๋ด๋ถ ์ต์ ํ" ๋๋ฌธ๋ง์ผ๊น์?
key๊ฐ "์ฃผ๋ฏผ๋ฑ๋ก๋ฒํธ" ๋ผ๋ฉด, ๋ฒํธ๊ฐ ๋ฐ๋๋ฉด ์ด๋ค ์ผ์ด ์๊ธธ๊น์?
key โ Key (ํค) ๋ ๋ผํด์ด clavis(์ด์ ) ์์ ์จ ๋จ์ด์์. React ์์ key ๋ ํ์ ์ปดํฌ๋ํธ๋ค ์ฌ์ด์์ ํน์ ์ธ์คํด์ค๋ฅผ ๊ตฌ๋ณํ๋ ๊ณ ์ ์๋ณ์์์.
๋๋ถ๋ถ์ ๊ฐ๋ฐ์๋ key ๋ฅผ "๋ฆฌ์คํธ ๋ ๋๋ง ์ ๊ฒฝ๊ณ ๋ฅผ ์์ ๊ธฐ ์ํ ๊ท์ฐฎ์ ๊ฒ" ์ ๋๋ก ์์์. ํ์ง๋ง key ์ ์ง์ง ์ญํ ์ ํจ์ฌ ๊ฐ๋ ฅํด์:
key ๋ "๊ฐ์ ์์น, ๊ฐ์ ํ์
" ์ด๋ผ๋ ๋ค๋ฅธ ์ธ์คํด์ค์์ ๋ช
์์ ์ผ๋ก ์ ์ธํ๋ ์๋จ์ด์์.
// โ
key๋ก ์์ฒ ์ด์ Scoreboard ๋ฒ๊ทธ ์๋ฒฝ ํด๊ฒฐ
function Scoreboard() {
const [isPlayerA, setIsPlayerA] = useState(true);
return (
<div>
{isPlayerA ? (
<Counter
key="์ํธ" // key="์ํธ" โ ์ด ์๋ฆฌ์ ์ฃผ๋ฏผ๋ฑ๋ก๋ฒํธ
person="์ํธ"
/>
) : (
<Counter
key="์์ฒ " // key="์์ฒ " โ ๋ค๋ฅธ ์ฃผ๋ฏผ๋ฑ๋ก๋ฒํธ โ ์์ ํ ๋ค๋ฅธ ์ธ์คํด์ค!
person="์์ฒ "
/>
)}
{/*
isPlayerA๊ฐ true โ false ๋ก ๋ฐ๋ ๋:
- ์ด์ ํธ๋ฆฌ: Counter (key="์ํธ", score=5)
- ์ ํธ๋ฆฌ: Counter (key="์์ฒ ")
- React ํ๋จ: "key๊ฐ ๋ค๋ฅด๋ค! ์์ ํ ๋ค๋ฅธ ์ธ์คํด์ค๋ค!"
- ๋์: key="์ํธ" Counter ์ธ๋ง์ดํธ โ key="์์ฒ " Counter ์๋ก ๋ง์ดํธ
- ๊ฒฐ๊ณผ: score๋ 0์ผ๋ก ๋ฆฌ์
๋จ โ
*/}
<button onClick={() => setIsPlayerA(!isPlayerA)}>๋ค์ ํ๋ ์ด์ด</button>
</div>
);
}key ๋ฅผ ๋ถ์ฌํ๋ฉด React ๋ ํ์
์ด ๊ฐ๋๋ผ๋ "๋ค๋ฅธ key = ๋ค๋ฅธ ์ธ์คํด์ค" ๋ก ์ธ์ํด์. ์ด์ key ๋ฅผ ๊ฐ์ง ์ปดํฌ๋ํธ๋ฅผ ์ธ๋ง์ดํธํ๊ณ , ์ key ๋ฅผ ๊ฐ์ง ์ปดํฌ๋ํธ๋ฅผ ์์ ํ ์๋ก ๋ง์ดํธํด์.
์ด ๋์์ ๋ค์ด์ด๊ทธ๋จ์ผ๋ก ๋ณด๋ฉด:
key="์ํธ" ์ผ ๋์ ํธ๋ฆฌ:
Scoreboard
โโโ div
โโโ Counter [key="์ํธ"] (score=5)
key="์์ฒ " ๋ก ๋ณ๊ฒฝ ํ ํธ๋ฆฌ:
Scoreboard
โโโ div
โโโ Counter [key="์์ฒ "] (score=0) โ ์๋ก ๋ง์ดํธ! score=0์์ ์์
// React๋ key="์ํธ" Counter๋ฅผ ์์ ํ ์ธ๋ง์ดํธ(ํ๊ดด)ํ๊ณ
// key="์์ฒ " Counter๋ฅผ ์ฒ์๋ถํฐ ์๋ก ๋ง์ดํธํ๋ค
๐ ์ฉ์ด: ์ฌ์กฐ์ (Reconciliation)
React ๊ฐ ์ด์ ๋ ๋ ํธ๋ฆฌ์ ์ ๋ ๋ ํธ๋ฆฌ๋ฅผ ๋น๊ต(diff)ํด์ ์ต์ํ์ DOM ์ ๋ฐ์ดํธ๋ง ์ํํ๋ ๊ณผ์ ์ด์์.key๋ ์ด ์ฌ์กฐ์ ๊ณผ์ ์์ "๊ฐ์ ๋์ด๋" ๋ฅผ ํ๋จํ๋ ํต์ฌ ๊ธฐ์ค์ด์์.
key ๊ฐ ์์ ๋ ๋ชฉ๋ก ๋ ๋๋ง์ด ์ํํ ์ด์
// โ key ์๋ ๋ชฉ๋ก ๋ ๋๋ง์ ์ฌ์
const studyMembers = ['์์', '์์ฒ ', '์ํธ'];
function MemberList() {
return (
<ul>
{studyMembers.map((name) => (
<MemberItem name={name} /> // key ์์!
))}
</ul>
);
}
// ๋ง์ฝ '์์'๋ฅผ ๋ชฉ๋ก ์์์ ์ ๊ฑฐํ๋ฉด:
// ๋ณ๊ฒฝ ์ : ['์์', '์์ฒ ', '์ํธ']
// ๋ณ๊ฒฝ ํ: ['์์ฒ ', '์ํธ']
// React๋ position์ผ๋ก๋ง ๋น๊ต:
// ์์น 0: '์์' MemberItem โ '์์ฒ ' MemberItem (์
๋ฐ์ดํธ)
// ์์น 1: '์์ฒ ' MemberItem โ '์ํธ' MemberItem (์
๋ฐ์ดํธ)
// ์์น 2: '์ํธ' MemberItem โ ์์ (์ ๊ฑฐ)
// ๊ฒฐ๊ณผ: 3๊ฐ๊ฐ 2๊ฐ์ ์
๋ฐ์ดํธ + 1๊ฐ ์ ๊ฑฐ๋ก ์ฒ๋ฆฌ๋จ
// ๋ง์ฝ MemberItem ์์ ์ํ๊ฐ ์๋ค๋ฉด ์ํ๊ฐ ์๋ฑํ ์์ดํ
์ผ๋ก ์ฎ๊ฒจ๊ฐ๋ ๋ฒ๊ทธ ๋ฐ์!// โ
key ์๋ ๋ชฉ๋ก ๋ ๋๋ง โ ์ฌ๋ฐ๋ฅธ ๋ฐฉ์
function MemberList() {
return (
<ul>
{studyMembers.map((name) => (
<MemberItem key={name} name={name} /> // key๋ก ์ ์ ๋ช
ํํ ํ์
))}
</ul>
);
}
// ์ด์ React๋ key๋ก ๋๊ฐ ๋๊ตฐ์ง ์ถ์ :
// key="์์" ์ ๊ฑฐ โ ํด๋น ์ธ์คํด์ค๋ง ์ธ๋ง์ดํธ
// key="์์ฒ ", key="์ํธ" ๋ ์์น๊ฐ ๋ฐ๋์ด๋ ์ํ ์ ์ง๋จ โ
๐ก ํ ์ค๋ก ๊ธฐ์ตํ๊ธฐ
key๋ ์ฃผ๋ฏผ๋ฑ๋ก๋ฒํธ์์. ๋ฒํธ๊ฐ ๋ฐ๋๋ฉด React ๋ ๊ธฐ์กด ๊ฑฐ์ฃผ์๋ฅผ ์ซ์๋ด๊ณ (์ธ๋ง์ดํธ) ์ ์ฌ๋์ ์ ์ฃผ์์ผ์(์ ๋ง์ดํธ). ๋ฒํธ๊ฐ ๊ฐ์ผ๋ฉด ์ด์ฌ๊ฐ๋(์์น ์ด๋) ๊ฐ์ ์ฌ๋์ผ๋ก ์ธ์ํด์.
๐งฉ key ํธ๋ฆญ ์ค๋ฌด ํจํด ๐ก
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- ํผ ์ ์ถ ํ ๊ฐ์ ๋ฆฌ์ ์
key๋ฅผ ํ์ฉํ๋ ๋ฐฉ๋ฒ์ ์ค๋ฌด์ ๋ฐ๋ก ์ ์ฉํ ์ ์์ด์.- ์ฑํ ์ฐฝ์ฒ๋ผ "๊ฐ์ ์ปดํฌ๋ํธ์ธ๋ฐ ๋ค๋ฅธ ์ธ์คํด์ค์ฌ์ผ ํ๋" ์ผ์ด์ค๋ฅผ ์ฒ๋ฆฌํ ์ ์์ด์.
ํจํด 1: ํผ ๊ฐ์ ๋ฆฌ์ ๐
์์๋ค ์ปค๋ฎค๋ํฐ์์ ๋๊ธ์ ์์ฑํ๋ฉด ํผ์ด ์๋์ผ๋ก ๋น์์ ธ์ผ ํด์. ๊ฐ์ฅ ๋จ์ํ ํด๊ฒฐ์ฑ
์ key ๋ฅผ ์ ์ถํ ๋๋ง๋ค ๋ฐ๊พธ๋ ๊ฑฐ์์.
// โ
ํจํด 1: key๋ฅผ ์ซ์๋ก ๊ด๋ฆฌํด์ ์ ์ถ ์ ํผ ๊ฐ์ ๋ฆฌ์
function CommentSection({ postId }) {
const [formKey, setFormKey] = useState(0); // ํผ์ ์ฃผ๋ฏผ๋ฑ๋ก๋ฒํธ ์ญํ
const handleCommentSubmit = async (content) => {
await submitComment({ postId, content }); // API ํธ์ถ
setFormKey((prev) => prev + 1);
// key๊ฐ 0 โ 1 ๋ก ๋ฐ๋ โ React๋ ์์ ํ ์ PostCommentForm์ ๋ง์ดํธ
// PostCommentForm ๋ด๋ถ์ ๋ชจ๋ useState๊ฐ ์ด๊ธฐ๊ฐ์ผ๋ก ๋ฆฌ์
๋จ
};
return (
<div>
<h3>๋๊ธ ์์ฑ</h3>
<PostCommentForm
key={formKey} // formKey๊ฐ ๋ฐ๋ ๋๋ง๋ค ์ ์ธ์คํด์ค๋ก ๊ต์ฒด๋จ
onSubmit={handleCommentSubmit}
/>
</div>
);
}
// PostCommentForm ๋ด๋ถ (์ํ๊ฐ ์ฌ๋ฌ ๊ฐ์ผ ๋ ํนํ ์ ์ฉ)
function PostCommentForm({ onSubmit }) {
const [content, setContent] = useState(''); // ๋๊ธ ๋ด์ฉ
const [isAnonymous, setIsAnonymous] = useState(false); // ์ต๋ช
์ฌ๋ถ
const [mood, setMood] = useState('neutral'); // ๊ฐ์ ํ๊ทธ
// key๊ฐ ๋ฐ๋๋ฉด ์ด ์ํ๋ค์ด ๋ชจ๋ ์ด๊ธฐ๊ฐ์ผ๋ก ๋ฆฌ์
๋จ
// ์ผ์ผ์ด setContent(''), setIsAnonymous(false), setMood('neutral') ์ ํด๋ ๋จ!
return (
<form onSubmit={(e) => { e.preventDefault(); onSubmit(content); }}>
<textarea value={content} onChange={(e) => setContent(e.target.value)} />
{/* ... ๊ธฐํ ํ๋๋ค */}
<button type="submit">๋๊ธ ์์ฑ</button>
</form>
);
}์ ์ด ๋ฐฉ์์ด ์ข์๊ฐ? PostCommentForm ๋ด๋ถ ์ํ๊ฐ 5๊ฐ๋ 10๊ฐ๋ key ํ๋๋ง ๋ฐ๊พธ๋ฉด ๋ชจ๋ ํ ๋ฒ์ ๋ฆฌ์
๋ผ์. ์ผ์ผ์ด setState ๋ก ์ด๊ธฐํํ๋ ์ฝ๋๊ฐ ํ์ ์์ด์.
ํจํด 2: ์ฑํ ์ฐฝ ์ธ์คํด์ค ๋ถ๋ฆฌ ๐๏ธ
์์๋ค ์ปค๋ฎค๋ํฐ์๋ 1:1 ์ฑํ ๊ธฐ๋ฅ์ด ์์ด์. ์ํธ์ ๋ํํ๋ค๊ฐ ์์ฒ ๋ก ์ ํํ๋ฉด, ์ฑํ ์ ๋ ฅ์ฐฝ์ด ๋น์์ง๊ณ ์์ฒ ๊ณผ์ ๋ํ ์ด๋ ฅ์ด ๋ก๋๋์ด์ผ ํด์.
// โ
ํจํด 2: ์ฐ๋ฝ์ฒ๋ณ ChatWindow๋ฅผ ์์ ํ ๋ค๋ฅธ ์ธ์คํด์ค๋ก ๋ถ๋ฆฌ
function DirectMessagePage() {
const [selectedContact, setSelectedContact] = useState('์ํธ');
return (
<div style={{ display: 'flex' }}>
{/* ์ฐ๋ฝ์ฒ ๋ชฉ๋ก */}
<ContactList
onSelect={setSelectedContact}
selected={selectedContact}
/>
{/*
key={selectedContact}๋ก ์ฐ๋ฝ์ฒ๋ง๋ค ์์ ํ ๋ค๋ฅธ ChatWindow ์ธ์คํด์ค ์์ฑ
์ํธ โ ์์ฒ ์ ํ ์:
- key="์ํธ" ChatWindow ์ธ๋ง์ดํธ (์
๋ ฅ ์ค์ธ ๋ด์ฉ, ์คํฌ๋กค ์์น ๋ฑ ๋ชจ๋ ์ด๊ธฐํ)
- key="์์ฒ " ChatWindow ์๋ก ๋ง์ดํธ (์์ฒ ๊ณผ์ ๋ํ ์ด๋ ฅ ๋ก๋ ์์)
*/}
<ChatWindow
key={selectedContact} // ์ฐ๋ฝ์ฒ๊ฐ ๋ฐ๋๋ฉด ์์ ํ ์ ์ฑํ
์ฐฝ์ ์ด์ด์
contact={selectedContact}
/>
</div>
);
}
function ChatWindow({ contact }) {
const [message, setMessage] = useState(''); // ํ์ฌ ์
๋ ฅ ์ค์ธ ๋ฉ์์ง
const [messages, setMessages] = useState([]); // ๋ํ ์ด๋ ฅ
// contact๊ฐ ๋ฐ๋๋ฉด(= key๊ฐ ๋ฐ๋๋ฉด) ์ด useEffect๊ฐ ์ ์ธ์คํด์ค์์ ์คํ๋จ
// message, messages ๋ชจ๋ ์ด๊ธฐ๊ฐ์์ ์์
useEffect(() => {
loadChatHistory(contact).then(setMessages); // ํด๋น ์ฐ๋ฝ์ฒ์ ์ฑํ
์ด๋ ฅ ๋ก๋
}, []); // ๋ง์ดํธ ์ ํ ๋ฒ๋ง ์คํ (key ๋ณ๊ฒฝ = ์ ๋ง์ดํธ์ด๋ฏ๋ก OK)
return (
<div>
<h2>{contact} ์์ ๋ํ</h2>
{/* ๋ํ ์ด๋ ฅ ํ์ */}
<div>{messages.map((msg) => <MessageBubble key={msg.id} msg={msg} />)}</div>
{/* ๋ฉ์์ง ์
๋ ฅ */}
<input value={message} onChange={(e) => setMessage(e.target.value)} />
</div>
);
}ํจํด 3: ๋์ผ ํ์ด์ง ๋ด ์กฐ๊ฑด๋ถ ์ปดํฌ๋ํธ ์ํ ๋ถ๋ฆฌ ๐
์คํฐ๋ ๊ฒ์๊ธ ์์ธ ํ์ด์ง์์ ํธ์ง ๋ชจ๋์ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๋ชจ๋๊ฐ ์์ด์. ํธ์ง ๋ชจ๋๋ก ์ ๋ ฅํ๋ค๊ฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ก ๊ฐ๋ค๊ฐ ๋์์ค๋ฉด ์ ๋ ฅ ๋ด์ฉ์ด ๋จ์์์ด์ผ ํด์. ํ์ง๋ง ์์ ๋ค๋ฅธ ํฌ์คํธ ํ์ด์ง๋ก ์ด๋ ํ ๋์์ค๋ฉด ์ด๊ธฐํ๋์ด์ผ ํด์.
// โ
ํจํด 3: postId๋ฅผ key๋ก ์ฌ์ฉํด์ ํฌ์คํธ๋ณ ์ํ ๊ฒฉ๋ฆฌ
function PostEditor({ postId }) {
// postId๊ฐ ๋ฐ๋๋ฉด(๋ค๋ฅธ ํฌ์คํธ๋ฅผ ํธ์งํ๋ฌ ๊ฐ๋ฉด) ์๋ํฐ ์ ์ฒด๊ฐ ๋ฆฌ์
๋จ
return (
<PostEditorContent
key={postId} // postId = ์๋ํฐ์ ์ ์์ฆ๋ช
์
postId={postId}
/>
);
}
function PostEditorContent({ postId }) {
const [title, setTitle] = useState(''); // ์ ๋ชฉ ์
๋ ฅ ์ํ
const [content, setContent] = useState(''); // ๋ด์ฉ ์
๋ ฅ ์ํ
const [isPreview, setIsPreview] = useState(false); // ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๋ชจ๋
// postId๊ฐ ๊ฐ์ผ๋ฉด(๊ฐ์ ํฌ์คํธ๋ฅผ ๊ณ์ ํธ์ง) ์ํ ์ ์ง
// postId๊ฐ ๋ฌ๋ผ์ง๋ฉด(๋ค๋ฅธ ํฌ์คํธ๋ก ์ด๋) key ๋ณ๊ฒฝ โ ์ํ ๋ฆฌ์
โ ์ด useEffect ์ฌ์คํ
useEffect(() => {
loadPostData(postId).then(({ title, content }) => {
setTitle(title); // ๊ธฐ์กด ํฌ์คํธ ๋ฐ์ดํฐ๋ก ์ด๊ธฐํ
setContent(content);
});
}, []); // ๋ง์ดํธ ์ ํ ๋ฒ๋ง (key ๋ณ๊ฒฝ = ์ ๋ง์ดํธ)
return isPreview
? <PostPreview title={title} content={content} />
: <PostForm title={title} content={content} onTitleChange={setTitle} onContentChange={setContent} />;
}ํจํด 4: ๊ฒ์์ด๋ณ ๊ฒฐ๊ณผ ๋ชฉ๋ก ์ํ ๋ถ๋ฆฌ ๐
// โ
ํจํด 4: ๊ฒ์์ด๊ฐ ๋ฐ๋ ๋๋ง๋ค ๊ฒฐ๊ณผ ๋ชฉ๋ก ์ํ๋ฅผ ๋ฆฌ์
function SearchPage() {
const [query, setQuery] = useState('');
const [submittedQuery, setSubmittedQuery] = useState('');
return (
<div>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="์คํฐ๋ ๊ฒ์..."
/>
<button onClick={() => setSubmittedQuery(query)}>๊ฒ์</button>
{/*
submittedQuery๊ฐ ๋ฐ๋ ๋๋ง๋ค SearchResults๋ฅผ ์์ ํ ์ ์ธ์คํด์ค๋ก ๊ต์ฒด
โ SearchResults ๋ด๋ถ์ ํ์ด์ง๋ค์ด์
์ํ(currentPage), ์ ๋ ฌ ์ํ ๋ฑ์ด ๋ฆฌ์
๋จ
*/}
{submittedQuery && (
<SearchResults
key={submittedQuery} // ๊ฒ์์ด๋ง๋ค ์ ์ธ์คํด์ค!
query={submittedQuery}
/>
)}
</div>
);
}๐ก ํ ์ค๋ก ๊ธฐ์ตํ๊ธฐ
keyํธ๋ฆญ์ ํต์ฌ์ "์ธ์ ์ํ๋ฅผ ๋ฆฌ์ ํ ์ง" ๋ฅผ ๊ฒฐ์ ํ๋ ๊ธฐ์ค๊ฐ์key์ ๋ฃ๋ ๊ฑฐ์์. ์ ์ถ ์นด์ดํฐ, ์ฐ๋ฝ์ฒ ID, ํฌ์คํธ ID, ๊ฒ์์ด โ ์ด ๊ธฐ์ค๊ฐ์ด ๋ฐ๋๋ฉด ์ปดํฌ๋ํธ๋ ์๋ก ํ์ด๋์.
๐งฉ ์ ๋ ๊ธ์ง: ๋ ๋ ํจ์ ์์ ์ปดํฌ๋ํธ ์ค์ฒฉ ์ ์ ๐ด
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- ๋ ๋ ํจ์ ์์ ์ปดํฌ๋ํธ๋ฅผ ์ ์ํ๋ฉด ์ ์ํ๊ฐ ๋งค๋ฒ ํญ๋ฐํ๋์ง ๋ด๋ถ ์๋ฆฌ๋ฅผ ์ค๋ช ํ ์ ์์ด์.
- ์ด ์ํฐ ํจํด์ด ์ ์ฑ๋ฅ ๋ฌธ์ ๋ฟ ์๋๋ผ ์ํ ๋ฒ๊ทธ๋ฅผ ์ ๋ฐํ๋์ง ์ ์ ์์ด์.
์ด๊ฑด React ํ์ต์์ ๊ฐ์ฅ ์น๋ช ์ ์ด๊ณ ์ง๋จํ๊ธฐ ์ด๋ ค์ด ๋ฒ๊ทธ ํจํด ์ค ํ๋์์. ์ฝ๋ ๋ฆฌ๋ทฐ์์๋ ๋์น๊ธฐ ์ฌ์ด ์ง๋ขฐ์์.
// ๐จ ์ ๋ ๊ธ์ง ํจํด โ ๋ ๋ ํจ์ ์์ ์ปดํฌ๋ํธ ์ ์
function StudyPostList() {
const [searchQuery, setSearchQuery] = useState('');
// ๐ฃ ๋งค ๋ ๋๋ง๋ค ์๋ก์ด ํจ์ ์ฐธ์กฐ๊ฐ ๋ง๋ค์ด์ง!
// StudyPostList๊ฐ ๋ ๋๋ง๋ ๋๋ง๋ค SearchInput์ ์์ ํ ์๋ก์ด ํจ์๊ฐ ๋ผ์
function SearchInput() {
const [inputValue, setInputValue] = useState('');
return (
<input
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="์คํฐ๋ ๊ฒ์..."
/>
);
}
const posts = filterPosts(allPosts, searchQuery); // ๊ฒ์ ํํฐ๋ง
return (
<div>
{/*
SearchInput์ ๋งค ๋ ๋๋ง๋ค ์๋ก์ด ํจ์ ์ฐธ์กฐ = ์๋ก์ด ์ปดํฌ๋ํธ ํ์
React ํ๋จ: "์ด์ ๋ ๋์ SearchInput๊ณผ ์ด๋ฒ ๋ ๋์ SearchInput์ ๋ค๋ฅธ ํ์
์ด๋ค!"
๋์: ๋งค ๋ ๋๋ง๋ค SearchInput์ ์ธ๋ง์ดํธ ํ ์๋ก ๋ง์ดํธ
๊ฒฐ๊ณผ: inputValue ์ํ๊ฐ ๋งค ๋ ๋๋ง๋ค '' ๋ก ๋ฆฌ์
โ ๊ธ์๋ฅผ ์
๋ ฅํ ์ ์์! ๐ฑ
*/}
<SearchInput />
<ul>
{posts.map((post) => (
<PostItem key={post.id} post={post} />
))}
</ul>
</div>
);
}์ ์ด๋ฐ ์ผ์ด ๋ฒ์ด์ง๋์?
JavaScript ์์ ํจ์๋ ๋งค๋ฒ ํธ์ถ๋ ๋๋ง๋ค ์๋ก์ด ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด์. StudyPostList ๊ฐ ๋ ๋๋ง๋ ๋๋ง๋ค SearchInput ์ด๋ผ๋ ๋ณ์์ ์์ ํ ์๋ก์ด ํจ์ ๊ฐ์ฒด๊ฐ ๋ด๊ฒจ์.
React ๋ ์ปดํฌ๋ํธ ํ์
์ ์ฐธ์กฐ(reference) ๋ก ๋น๊ตํด์. ์ด์ ๋ ๋์ SearchInput ๊ณผ ์ด๋ฒ ๋ ๋์ SearchInput ์ ์ด๋ฆ์ด ๊ฐ์๋ ๋ฉ๋ชจ๋ฆฌ ์ฃผ์๊ฐ ๋ฌ๋ผ์. ๊ทธ๋์ React ๋ "์์ ํ ๋ค๋ฅธ ํ์
์ด ์๋ค!" ๋ผ๊ณ ํ๋จํ๊ณ ์ธ๋ง์ดํธ ํ ์๋ก ๋ง์ดํธํด์.
// ๐งช ๊ฐ๋
์ฆ๋ช
(์์ฌ ์ฝ๋)
const render1 = () => {
function SearchInput() { ... } // ์ฃผ์: 0x1234
return <SearchInput />;
};
const render2 = () => {
function SearchInput() { ... } // ์ฃผ์: 0x5678 (๋ค๋ฅธ ํจ์ ๊ฐ์ฒด!)
return <SearchInput />;
};
// React๊ฐ ๋ณด๋ ๊ฒ:
// render1์ SearchInput: type = 0x1234
// render2์ SearchInput: type = 0x5678
// ๊ฒฐ๋ก : ํ์
์ด ๋ฌ๋ผ์ก๋ค โ ์ธ๋ง์ดํธ + ์ ๋ง์ดํธ!์ด ๋ฒ๊ทธ๋ ์ ๋ ฅ์ฐฝ์ ๊ธ์๋ฅผ ์น๋ฉด ํฌ์ปค์ค๊ฐ ์ฌ๋ผ์ง๊ฑฐ๋ ์ ๋ ฅํ ๊ธ์๊ฐ ์ฆ์ ์ฌ๋ผ์ง๋ ์ฆ์์ผ๋ก ๋ํ๋์. ์์ธ์ ๋ชจ๋ฅด๋ฉด ๊ต์ฅํ ํฉ๋นํ ๋ฒ๊ทธ์์.
// โ
์ฌ๋ฐ๋ฅธ ํด๊ฒฐ์ฑ
: ์ปดํฌ๋ํธ๋ฅผ ๋ ๋ ํจ์ ๋ฐ๊นฅ์ ์ ์
// SearchInput์ ๋ชจ๋ ์ต์์ ๋ ๋ฒจ์ ์ ์ โ ํจ์ ์ฐธ์กฐ๊ฐ ๋ถ๋ณ(stable reference)
function SearchInput({ onSearch }) {
const [inputValue, setInputValue] = useState('');
return (
<input
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && onSearch(inputValue)}
placeholder="์คํฐ๋ ๊ฒ์..."
/>
);
}
// StudyPostList๋ ์ด์ SearchInput์ importํด์ ์ฌ์ฉ
function StudyPostList() {
const [searchQuery, setSearchQuery] = useState('');
const posts = filterPosts(allPosts, searchQuery);
return (
<div>
{/* SearchInput์ ํญ์ ๊ฐ์ ํจ์ ์ฐธ์กฐ โ ๋ ๋๋ง๋ค ์ฌ์ฌ์ฉ โ ์ํ ์ ์ง โ
*/}
<SearchInput onSearch={setSearchQuery} />
<ul>
{posts.map((post) => (
<PostItem key={post.id} post={post} />
))}
</ul>
</div>
);
}Before / After ํ๋์ ๋น๊ต:
| ๊ตฌ๋ถ | โ ๋ ๋ ํจ์ ๋ด๋ถ ์ ์ | โ ๋ชจ๋ ์ต์์ ์ ์ |
|---|---|---|
| ํจ์ ์ฐธ์กฐ | ๋งค ๋ ๋๋ง๋ค ์ ๊ฐ์ฒด | ๋ชจ๋ ๋ก๋ ์ ๋จ ํ ๋ฒ ์์ฑ |
| React ์ธ์ | ๋งค๋ฒ "์ ํ์ " | ํญ์ "๊ฐ์ ํ์ " |
| ์ปดํฌ๋ํธ ๋์ | ๋งค ๋ ๋๋ง๋ค ์ธ๋ง์ดํธ + ์ ๋ง์ดํธ | ์ํ ์ ์งํ๋ฉฐ ์ ๋ฐ์ดํธ |
| ์ํ | ๋งค ๋ ๋๋ง๋ค ์ด๊ธฐํ | ์ ์ ์ ์ง |
| ์ฑ๋ฅ | ๋งค๋ฒ DOM ์ฌ์์ฑ | ์ต์ํ์ DOM ์ ๋ฐ์ดํธ |
ํ์ดํ ํจ์๋ ๋์ผํ ๊ท์น ์ ์ฉ:
// โ ์ด๊ฒ๋ ์ ๋ ๊ธ์ง โ ํ์ดํ ํจ์๋ก ์ปดํฌ๋ํธ ์ ์๋ ๋์ผํ ๋ฌธ์
function ParentComponent() {
const ChildComponent = () => { // ๋งค ๋ ๋๋ง๋ค ์ ํจ์ ๊ฐ์ฒด
const [value, setValue] = useState(0);
return <button onClick={() => setValue(v => v + 1)}>{value}</button>;
};
return <ChildComponent />; // ์ํ๊ฐ ๋งค๋ฒ ๋ฆฌ์
๋จ ๐ฃ
}
// โ
๋ฐ๊นฅ์ผ๋ก ๊บผ๋ด๊ธฐ
const ChildComponent = () => { // ํ ๋ฒ๋ง ์์ฑ๋จ
const [value, setValue] = useState(0);
return <button onClick={() => setValue(v => v + 1)}>{value}</button>;
};
function ParentComponent() {
return <ChildComponent />; // ํญ์ ๊ฐ์ ์ฐธ์กฐ โ ์ํ ์ ์ง โ
}๐ก ํ ์ค๋ก ๊ธฐ์ตํ๊ธฐ
์ปดํฌ๋ํธ๋ ๋ชจ๋์ ์ต์์ ๋ ๋ฒจ์๋ง ์ ์ํด์. ๋ ๋ ํจ์ ์์ ์ปดํฌ๋ํธ๋ฅผ ์ฐ๋ฉด ๋งค ๋ ๋๋ง๋ค "์ ์ฃผ๋ฏผ๋ฑ๋ก๋ฒํธ" ๋ฅผ ๋ฐ๊ธ๋ฐ์์ ์ํ๊ฐ ํญ๋ฐํด์.
๐ฅ ์๋ฌ ํด๊ฒฐ ์นดํ๋ก๊ทธ
์๋ฌ๋ ์ด์ํ ๋์์ด ๋ฐ์ํ๋ฉด Ctrl+F ๋ก ์ฆ์์ ํค์๋๋ฅผ ๊ฒ์ํด๋ด์. ๋๋ถ๋ถ ์ฌ๊ธฐ ์์ด์.
โ ์ฆ์: ์ ๋ ฅ์ฐฝ์ ๊ธ์๋ฅผ ์น๋ฉด ํฌ์ปค์ค๊ฐ ์ฌ๋ผ์ง๊ฑฐ๋ ์ ๋ ฅํ ๋ด์ฉ์ด ์ฆ์ ์ง์์ง๋ค
์ธ์ ๋์ค๋๊ฐ?
์
๋ ฅ์ฐฝ์ ํด๋ฆญํด์ 'ใ
' ์ ์
๋ ฅํ๋ฉด ํฌ์ปค์ค๊ฐ ์ฌ๋ผ์ง๊ฑฐ๋
์
๋ ฅ ํ ์ปดํฌ๋ํธ๊ฐ unmount/remount ๋๋ ๊ฒ์ฒ๋ผ ๋ณด์
์์ธ: ๋ ๋ ํจ์ ์์ ์ ๋ ฅ ์ปดํฌ๋ํธ๋ฅผ ์ ์ํด์ ๋งค ๋ ๋๋ง๋ค ์ ์ปดํฌ๋ํธ ํ์ ์ผ๋ก ๊ต์ฒด๋๋ ์ํฉ
์ง๋จ ์ฝ๋:
// ์ด๋ฐ ํจํด์ด ์์ผ๋ฉด ๋ฒ์ธ
function ParentComponent() {
// โ ๏ธ ๋ ๋ ์์ ์ปดํฌ๋ํธ ์ ์ ๋ฐ๊ฒฌ!
function InputField() {
const [text, setText] = useState('');
return <input value={text} onChange={e => setText(e.target.value)} />;
}
return <InputField />; // ๋งค ๋ ๋๋ง๋ค ๋ฆฌ์
๋จ
}ํด๊ฒฐ์ฑ :
// InputField๋ฅผ ํ์ผ ์ต์์๋ก ๊บผ๋ด๊ธฐ
function InputField() {
const [text, setText] = useState('');
return <input value={text} onChange={e => setText(e.target.value)} />;
}
function ParentComponent() {
return <InputField />; // ์ด์ ์์ ์ ์ผ๋ก ์๋
}์ ์ด๋ฐ ์ผ์ด ์๊ธฐ๋์?
์
๋ ฅํ ๋๋ง๋ค ์์ ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋ง๋๊ณ (์ํ ๋ณ๊ฒฝ ๋๋ฌธ์), ๊ทธ๋ฌ๋ฉด ๋ ๋ ํจ์ ์์์ ์ ํจ์ ๊ฐ์ฒด๊ฐ ๋ง๋ค์ด์ ธ์. React ๋ ํ์
์ด ๋ฌ๋ผ์ก๋ค๊ณ ํ๋จํด์ ์ธ๋ง์ดํธ + ์ ๋ง์ดํธ๋ฅผ ํด์. ์ด ์ฌ์ดํด์ด ์
๋ ฅํ ๋๋ง๋ค ๋ฐ๋ณต๋ผ์.
โ ์ฆ์: ํ๋ ์ด์ด/์ ์ ๋ฅผ ์ ํํ๋๋ฐ ์ด์ ์ฌ๋์ ์ํ(์ ์, ์ ๋ ฅ๊ฐ ๋ฑ)๊ฐ ๋จ์์๋ค
์์ธ: ๊ฐ์ ์์น์ ๊ฐ์ ์ปดํฌ๋ํธ ํ์ ์ ์กฐ๊ฑด๋ถ๋ก ๋ ๋๋งํ๊ณ ์์ด์ React ๊ฐ ๋์ผ ์ธ์คํด์ค๋ก ์ธ์
์ง๋จ ์ฝ๋:
// ์ด๋ฐ ํจํด์ด ์์ผ๋ฉด ๋ฒ์ธ
{isPlayerA ? <Counter person="์ํธ" /> : <Counter person="์์ฒ " />}
// ๊ฐ์ ์์น, ๊ฐ์ ํ์
โ ์ํ ์ ์ง๋จํด๊ฒฐ์ฑ :
// key๋ฅผ ์ถ๊ฐํด์ ์ธ์คํด์ค ๋ช
์์ ๋ถ๋ฆฌ
{isPlayerA
? <Counter key="์ํธ" person="์ํธ" />
: <Counter key="์์ฒ " person="์์ฒ " />
}โ ์ฆ์: ํผ ์ ์ถ ํ ์ ๋ ฅ ํ๋๊ฐ ๋น์์ง์ง ์๋๋ค
์์ธ: ํผ ์ ์ถ ํ ์ํ๋ฅผ ์๋์ผ๋ก ๋ฆฌ์ ํ์ง ์๊ฑฐ๋, ์ปดํฌ๋ํธ๊ฐ ๋์ผ ์ธ์คํด์ค๋ก ์ ์ง๋๋ ์ํฉ
ํด๊ฒฐ์ฑ A: key ํธ๋ฆญ (์ํ๊ฐ ๋ง์ ๋ ์ถ์ฒ):
function Parent() {
const [formKey, setFormKey] = useState(0);
const handleSubmit = async (data) => {
await submitData(data);
setFormKey(prev => prev + 1); // key ๋ณ๊ฒฝ โ ํผ ์ ์ฒด ๋ฆฌ์
};
return <MyForm key={formKey} onSubmit={handleSubmit} />;
}ํด๊ฒฐ์ฑ B: ์๋ ๋ฆฌ์ (์ํ๊ฐ 1-2๊ฐ์ผ ๋):
function MyForm({ onSubmit }) {
const [value, setValue] = useState('');
const handleSubmit = async () => {
await onSubmit(value);
setValue(''); // ์ ์ถ ํ ์๋ ๋ฆฌ์
};
return <input value={value} onChange={e => setValue(e.target.value)} />;
}โ ์ฆ์: ์ฐ๋ฝ์ฒ๋ฅผ ์ ํํ๋๋ฐ ์ด์ ๋ํ์ ๋ฏธ์ ์ก ๋ฉ์์ง๊ฐ ๋จ์์๋ค
์์ธ: ChatWindow ๊ฐ์ ์ปดํฌ๋ํธ์ key ๋ฅผ ์ฃผ์ง ์์์ ๊ฐ์ ์ธ์คํด์ค๋ฅผ ์ฌ์ฌ์ฉํ๊ณ ์์
ํด๊ฒฐ์ฑ :
<ChatWindow
key={selectedContact} // ์ฐ๋ฝ์ฒ๊ฐ ๋ฐ๋๋ฉด ์ ์ธ์คํด์ค
contact={selectedContact}
/>โ ๊ฒฝ๊ณ : Warning: Each child in a list should have a unique "key" prop.
์ธ์ ๋์ค๋๊ฐ?
Warning: Each child in a list should have a unique "key" prop.
at li
at PostItem
์์ธ: .map() ์ผ๋ก ๋ชฉ๋ก์ ๋ ๋๋งํ ๋ key prop ์ ๋๋ฝํ๊ฑฐ๋ ์ค๋ณต๋ key ๋ฅผ ์ฌ์ฉ
์ ๋ ํ๋ฉด ์ ๋๋ ๊ฒ:
// โ key ์์
posts.map(post => <PostItem post={post} />)
// โ index๋ฅผ key๋ก ์ฌ์ฉ (๋ชฉ๋ก ์์๊ฐ ๋ฐ๋ ์ ์๋ ๊ฒฝ์ฐ)
posts.map((post, index) => <PostItem key={index} post={post} />)
// ์ ๋ ฌ, ์ถ๊ฐ, ์ญ์ ๊ฐ ์ผ์ด๋๋ฉด key-์ธ์คํด์ค ๋งคํ์ด ์ํด
// โ ์ค๋ณต key
posts.map(post => <PostItem key="item" post={post} />)์ฌ๋ฐ๋ฅธ ํด๊ฒฐ์ฑ :
// โ
๊ณ ์ ํ๊ณ ์์ ์ ์ธ ID ์ฌ์ฉ
posts.map(post => <PostItem key={post.id} post={post} />)
// โ
index๋ ๋ชฉ๋ก์ด ์ ๋ ๋ณํ์ง ์์ ๋๋ง ํ์ฉ
staticItems.map((item, index) => <Item key={index} item={item} />)๐ ์ด๋ฒ์ ๋ฐฐ์ด ๋ด์ฉ ์ด์ ๋ฆฌ
์ค๋ ๋ฐฐ์ด ํต์ฌ์ ํ๋์ ์ ๋ฆฌํด๋ด์. ์ค๋ฌด์์ ๊ธธ์ ์์์ ๋ ์ด๊ฒ๋ง ๋ด๋ ๋ผ์.
๐ ํต์ฌ ์๋ฆฌ ์์ฝ
| ์๋ฆฌ | ๋ด์ฉ | ๊ธฐ์ต๋ฒ |
|---|---|---|
| ์ํ ๋ณด๊ด ์์น | ์ปดํฌ๋ํธ ํจ์ ์์ด ์๋, React ๊ฐ ๊ด๋ฆฌํ๋ ๋ ๋ ํธ๋ฆฌ์ ์์น์ ๋ณด๊ด | "์ข์ ๋ฒํธ์ ๋ถ์ ํฌ์คํธ์" |
| ๊ฐ์ ์์น + ๊ฐ์ ํ์ | ์ํ ๋ณด์กด, prop ๋ง ์ ๋ฐ์ดํธ | "๊ฐ์ ์๋ฆฌ, ๊ฐ์ ์์ โ ํฌ์คํธ์ ์ ์ง" |
| ๊ฐ์ ์์น + ๋ค๋ฅธ ํ์ | ๊ธฐ์กด ํธ๋ฆฌ ์ธ๋ง์ดํธ + ์ ๋ง์ดํธ โ ์ํ ์ด๊ธฐํ | "์์ ์ข ๋ฅ๊ฐ ๋ฐ๋๋ฉด ํฌ์คํธ์ ์๋ก ๋ถ์" |
| key ์ญํ | ๊ฐ์ ํ์ ์ด๋ผ๋ ๋ค๋ฅธ ์ธ์คํด์ค์์ ๋ช ์ โ ์ํ ๊ฐ์ ๋ฆฌ์ | "์ฃผ๋ฏผ๋ฑ๋ก๋ฒํธ ๊ต์ฒด" |
| ์ธ๋ง์ดํธ | ํธ๋ฆฌ์์ ์ ๊ฑฐ๋ ๋ ํด๋น ์์น์ ์ํ๋ ํจ๊ป ์ญ์ | "์๋ฆฌ ์์ ๋ฉด ํฌ์คํธ์๋ ๋ฒ๋ฆผ" |
๐ฏ key ์ฌ์ฉ ํจํด ์นํธ์ํธ
| ์ํฉ | key ๊ฐ | ํจ๊ณผ |
|---|---|---|
| ํ๋ ์ด์ด/์ ์ ์ ํ ์ ์ํ ๋ฆฌ์ | key={userId} | ์ ์ ๋ฐ๋๋ฉด ์ ์ธ์คํด์ค |
| ํผ ์ ์ถ ํ ๊ฐ์ ์ด๊ธฐํ | key={submitCount} | ์ ์ถ ์ ์นด์ดํฐ ์ฆ๊ฐ โ ์ ๋ง์ดํธ |
| ์ฐ๋ฝ์ฒ๋ณ ์ฑํ ์ฐฝ ๋ถ๋ฆฌ | key={contactId} | ์ฐ๋ฝ์ฒ ๋ฐ๋๋ฉด ์ ์ฑํ ์ฐฝ |
| ๊ฒ์์ด๋ณ ๊ฒฐ๊ณผ ๋ถ๋ฆฌ | key={searchQuery} | ๊ฒ์์ด ๋ฐ๋๋ฉด ๊ฒฐ๊ณผ ๋ฆฌ์ |
| ํฌ์คํธ๋ณ ์๋ํฐ ๋ถ๋ฆฌ | key={postId} | ํฌ์คํธ ๋ฐ๋๋ฉด ์๋ํฐ ์ด๊ธฐํ |
| ๋ชฉ๋ก ์์ดํ | key={item.id} | ์ ๋ ฌ/์ญ์ ์ ์ํ ์์ ์ ์ง |
โ ๏ธ ์ ๋ ํ์ง ๋ง ๊ฒ
| ์ํฉ | โ ๋์ ์ | โ ์ข์ ์ | ์ด์ |
|---|---|---|---|
| ์ปดํฌ๋ํธ ์ ์ ์์น | ๋ ๋ ํจ์ ์์ ์ ์ | ๋ชจ๋ ์ต์์์ ์ ์ | ๋งค ๋ ๋๋ง๋ค ์ ํ์ โ ์ํ ํญ๋ฐ |
| ๋ชฉ๋ก key ์ ํ | key={index} (๊ฐ๋ณ ๋ชฉ๋ก) | key={item.id} | ์์ ๋ณ๊ฒฝ ์ ์ํ ์ํด |
| ์ํ ๋ฆฌ์ ๋ฐฉ๋ฒ | ์กฐ๊ฑด๋ถ๋ก ๋ค๋ฅธ ๋ํผ ํ๊ทธ ์ฌ์ฉ | key ๋ณ๊ฒฝ | ์ฝ๋ ๊ฐ๋ ์ฑ ์ ํ |
| ํ์ ๋น๊ต ์คํด | prop ์ด ๋ค๋ฅด๋ฉด ๋ค๋ฅธ ์ปดํฌ๋ํธ | ํ์ ๊ณผ ์์น๊ฐ ๊ฐ์ผ๋ฉด ๋์ผ | ์ํ ์ ์ง ๋ฒ๊ทธ ๋ฐ์ |
๐ ์ํ ๋ณด์กด vs ๋ฆฌ์ ๊ฒฐ์ ํธ๋ฆฌ
์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง๋ ๋:
โโโ ์ด์ ๋ ๋์ ๊ฐ์ ์์น?
โ โโโ NO โ ์ ๋ง์ดํธ (์ด์ ์ํ ์ญ์ )
โ โโโ YES โ ๊ณ์ ๋น๊ต...
โ โโโ key๊ฐ ๊ฐ์๊ฐ?
โ โ โโโ NO โ ์ธ๋ง์ดํธ ํ ์ ๋ง์ดํธ (์ํ ๋ฆฌ์
)
โ โ โโโ YES โ ๊ณ์ ๋น๊ต...
โ โ โโโ ์ปดํฌ๋ํธ ํ์
์ด ๊ฐ์๊ฐ?
โ โ โ โโโ NO โ ์ธ๋ง์ดํธ ํ ์ ๋ง์ดํธ (์ํ ๋ฆฌ์
)
โ โ โ โโโ YES โ ๊ธฐ์กด ์ธ์คํด์ค ์ฌ์ฌ์ฉ (์ํ ๋ณด์กด) + prop ์
๋ฐ์ดํธ
๐ฃ ์์ฒ ์ด์ ํด๊ทผ ์ผ๊ธฐ
์ค๋ฅ ์์ด ์ ๋์๊ฐ๋ ์ปดํฌ๋ํธ์์ ๊ฐ์๊ธฐ ํฌ์ปค์ค๊ฐ ๋ ์๊ฐ๊ณ ์ํ๊ฐ ์ฆ๋ฐํ๋ ๋ฏธ์คํฐ๋ฆฌ๊ฐ ๋ ๋ ํธ๋ฆฌ ๊ฐ๋ ํ๋๋ก ๋ปฅ ๋ซ๋ ธ๋ค. ๋ด๊ฐ ์ปดํฌ๋ํธ ์์ ์ปดํฌ๋ํธ๋ฅผ ์ ์ํ๋ ๋์ฐํ ์ง์ ํ๊ณ ์์๋ค๋!
๐ก "์ํ๋ ์ปดํฌ๋ํธ ์์ ์๋ ๊ฒ ์๋๋ผ, ํธ๋ฆฌ์ '์์น'์ ์ธ๋ค์ด ์ฌ๋ ๊ฑฐ๋ค. Key๋ ๊ทธ ์ธ์ ์์ ์ ์ ์ ๊ณ (์ฃผ๋ฏผ๋ฑ๋ก๋ฒํธ)์ ๊ฐ๋ค!"
๋จ์ํ ๋ฆฌ์คํธ ๊ฒฝ๊ณ ์ง์ฐ๋ ค๊ณ ๋์ถฉ ๋ฃ๋ key={index}๊ฐ ์ผ๋ง๋ ์ํํ ์ํํญํ์ธ์ง ์์๋ค. ํผ ์ํ๋ฅผ ๋ ๋ ค๋ฒ๋ฆด ๋๋, ์ฑํ
์ฐฝ์ ์์ ํ ๊ต์ฒดํ ๋ key ํธ๋ฆญ์ ์ฐ๋ ๊ฑธ ์๊ณ ๋๋, ๋ญ๊ฐ ๋ฆฌ์กํธ ์์ง์ ๋ฉฑ์ด์ ์ก๊ณ ์กฐ์ข
ํ๋ ๋๋์ด ๋ค์ด ๋ฌํ๊ฒ ์พ๊ฐ์ด ์ธ๋ค.
๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
Q1. ์ํ ๋ณด์กด ์ฌ๋ถ ํ๋จ ๐ข
Q1. ์๋ ์ฝ๋์์ isAdmin ์ด true โ false ๋ก ๋ฐ๋๋ฉด UserPanel ์ ์ํ๋ ์ด๋ป๊ฒ ๋ ๊น์?
function App() {
const [isAdmin, setIsAdmin] = useState(true);
return (
<div>
{isAdmin ? <UserPanel role="admin" /> : <UserPanel role="viewer" />}
</div>
);
}- A) ์ํ๊ฐ ์์ ํ ๋ฆฌ์
๋๋ค.
roleprop ์ด ๋ฌ๋ผ์ก๊ธฐ ๋๋ฌธ์ด๋ค. - B) ์ํ๊ฐ ์ ์ง๋๋ค. ๊ฐ์ ์์น์ ๊ฐ์ ํ์
(
UserPanel) ์ด๊ธฐ ๋๋ฌธ์ด๋ค. - C) ์ํ๊ฐ ์ ์ง๋๋ค. React ๋ ํญ์ ์ํ๋ฅผ ๋ณด์กดํ๋ค.
- D) ์ํ๊ฐ ๋ฆฌ์ ๋๋ค. ์กฐ๊ฑด๋ถ ๋ ๋๋ง์ ํญ์ ์ธ๋ง์ดํธ๋ฅผ ์ ๋ฐํ๋ค.
โ ์ ๋ต: B โ ๊ฐ์ ์์น์ ๊ฐ์ ํ์ ์ด ์ค๋ฉด prop ๋ด์ฉ์ ์๊ด์์ด ์ํ๋ฅผ ๋ณด์กดํด์.
์ค๋ต ํด์ค:
- A โ React ๋ prop ์ ๋ด์ฉ์ด ์๋๋ผ ์ปดํฌ๋ํธ ํ์ ๊ณผ ํธ๋ฆฌ ์์น๋ฅผ ๋ด์.
role์ด ๋ฌ๋ผ์ ธ๋ ํ์ ์ด ๊ฐ์ผ๋ฉด ์ ๋ฐ์ดํธ๋ก ์ฒ๋ฆฌํด์.- C โ React ๊ฐ ํญ์ ์ํ๋ฅผ ๋ณด์กดํ๋ ๊ฑด ์๋์์. ํ์ ์ด ๋ฌ๋ผ์ง๊ฑฐ๋ key ๊ฐ ๋ฌ๋ผ์ง๋ฉด ๋ฆฌ์ ๋ผ์.
- D โ ์กฐ๊ฑด๋ถ ๋ ๋๋ง ์์ฒด๊ฐ ์ธ๋ง์ดํธ๋ฅผ ์ ๋ฐํ๋ ๊ฒ ์๋์์. ์กฐ๊ฑด์ด ๋ฐ๋์ด๋ ๊ฐ์ ์์น์ ๊ฐ์ ํ์ ์ด ์ค๋ฉด ์ฌ์ฌ์ฉํด์.
๐ ํต์ฌ ๊ธฐ์ต๋ฒ: React ๋ "ํ์ + ์์น + key" ๊ฐ ๋ชจ๋ ๊ฐ์์ผ ๋์ผ ์ธ์คํด์ค๋ก ์ธ์ํด์. prop ์ ํ๋ณ ๊ธฐ์ค์ด ์๋์์.
Q2. ๋น์นธ ์ฑ์ฐ๊ธฐ ๐ก
Q2. ์์๋ค ์ปค๋ฎค๋ํฐ ์ฑํ ๊ธฐ๋ฅ์์ ์ฐ๋ฝ์ฒ๋ฅผ ๋ฐ๊ฟ ๋๋ง๋ค ์ฑํ ์ฐฝ์ ์์ ํ ์ด๊ธฐํํ๊ณ ์ถ์ด์. ๋น์นธ์ ์ฑ์๋ณด์ธ์.
function ChatPage() {
const [activeContact, setActiveContact] = useState('์ํธ');
return (
<ChatWindow
_______________ // ์ฌ๊ธฐ์ ๋ฌด์์ ์จ์ผ ํ ๊น์?
contact={activeContact}
/>
);
}โ ์ ๋ต:
key={activeContact}ํด์ค:
key={activeContact}๋ฅผ ์ถ๊ฐํ๋ฉดactiveContact๊ฐ '์ํธ' ์์ '์์ฒ ' ๋ก ๋ฐ๋ ๋ key ๊ฐ์ด ๋ฌ๋ผ์ง๊ณ , React ๋ ์ด๋ฅผ ์์ ํ ์ ์ปดํฌ๋ํธ๋ก ์ธ์ํด์ ์ธ๋ง์ดํธ ํ ์๋ก ๋ง์ดํธํด์.ChatWindow๋ด๋ถ์ ๋ชจ๋ ์ํ(์ ๋ ฅ ์ค์ธ ๋ฉ์์ง, ์คํฌ๋กค ์์น ๋ฑ) ๊ฐ ์ด๊ธฐํ๋ผ์.๐ ํต์ฌ ๊ธฐ์ต๋ฒ: "์ฐ๋ฝ์ฒ = key" ํจํด์ด์์. ์ฐ๋ฝ์ฒ๋ง๋ค ๋ ๋ฆฝ๋ ์ฑํ ์ฐฝ ์ธ์คํด์ค๋ฅผ ์ํ๋ค๋ฉด ์ฐ๋ฝ์ฒ ID ๋ฅผ key ๋ก ์ฃผ๋ฉด ๋ผ์.
Q3. ๋๋ฒ๊น ํด์ฆ ๐ด
Q3. ์๋ ์ฝ๋์์ StudyFilter ๋ด๋ถ์ ํํฐ ์ํ๊ฐ ์
๋ ฅํ ๋๋ง๋ค ๋ฆฌ์
๋๋ ๋ฒ๊ทธ๊ฐ ์์ด์. ๋ฌธ์ ๋ฅผ ์ฐพ์๋ณด์ธ์.
function StudySearchPage() {
const [keyword, setKeyword] = useState('');
const [results, setResults] = useState([]);
// (A) ๋งค ๋ ๋๋ง๋ค ์๋ก ์์ฑ๋๋ ์ปดํฌ๋ํธ
const StudyFilter = () => {
const [selectedCategory, setSelectedCategory] = useState('all');
const [sortOrder, setSortOrder] = useState('latest');
return (
<div>
<select value={selectedCategory} onChange={e => setSelectedCategory(e.target.value)}>
<option value="all">์ ์ฒด</option>
<option value="frontend">ํ๋ก ํธ์๋</option>
</select>
<select value={sortOrder} onChange={e => setSortOrder(e.target.value)}>
<option value="latest">์ต์ ์</option>
<option value="popular">์ธ๊ธฐ์</option>
</select>
</div>
);
};
return (
<div>
<input
value={keyword}
onChange={e => setKeyword(e.target.value)} // (B) ์
๋ ฅ ์ StudySearchPage ๋ฆฌ๋ ๋๋ง
/>
<StudyFilter /> {/* (C) ๋ฆฌ๋ ๋๋ง๋ ๋๋ง๋ค ์ ํ์
์ผ๋ก ์ธ์๋จ */}
<SearchResults results={results} />
</div>
);
}์ด ์ฝ๋์ ๋ฌธ์ ์ ์? (ํด๋นํ๋ ๊ฒ ๋ชจ๋ ์ ํ)
- A)
(A)์์const StudyFilter = () =>์ฒ๋ผ ํ์ดํ ํจ์๋ก ์ ์ํด์ ๋ฌธ์ ๋ค. - B)
(B)์์setKeyword๋ฅผ ํธ์ถํ๋ฉดStudySearchPage๊ฐ ๋ฆฌ๋ ๋๋ง๋๊ณ , ๊ทธ๋ฌ๋ฉด(A)์์ ์StudyFilterํจ์ ๊ฐ์ฒด๊ฐ ๋ง๋ค์ด์ง๋ค. - C)
(C)์์<StudyFilter />๋ ๋ ๋๋ง๋ค ์ ํ์ ์ผ๋ก ์ธ์๋์ด ์ธ๋ง์ดํธ + ์ ๋ง์ดํธ๋๋ค. - D)
results์ํ๊ฐ ๋น์ด์์ด์ ๋ฌธ์ ๋ค.
โ ์ ๋ต: B, C
์ค๋ต ํด์ค:
- A โ ํ์ดํ ํจ์ ์์ฒด๊ฐ ๋ฌธ์ ๊ฐ ์๋์์. ๋ ๋ ํจ์ "์์ ์ ์๋๋ค๋ ๊ฒ" ์ด ๋ฌธ์ ์์.
function StudyFilter()๋ก ๋ฐ๊ฟ๋ ๋ ๋ ํจ์ ์์ ์์ผ๋ฉด ๋์ผํ๊ฒ ๋ง๊ฐ์ ธ์.- D โ
results์ํ๋ ํํฐ ์ํ ๋ฆฌ์ ๊ณผ ๋ฌด๊ดํด์.์ฌ๋ฐ๋ฅธ ํด๊ฒฐ์ฑ :
// StudyFilter๋ฅผ ํ์ผ ์ต์์๋ก ๊บผ๋ const StudyFilter = () => { const [selectedCategory, setSelectedCategory] = useState('all'); const [sortOrder, setSortOrder] = useState('latest'); // ... }; function StudySearchPage() { const [keyword, setKeyword] = useState(''); // StudyFilter๋ ์ด์ ์์ ์ ์ธ ์ฐธ์กฐ โ ํํฐ ์ํ ์ ์ง๋จ return ( <div> <input value={keyword} onChange={e => setKeyword(e.target.value)} /> <StudyFilter /> </div> ); }๐ ํต์ฌ ๊ธฐ์ต๋ฒ: ๋ ๋ ํจ์ ์์ ์ ์๋ ์ปดํฌ๋ํธ๋ ๋งค ๋ ๋๋ง๋ค ์ ์ฃผ๋ฏผ๋ฑ๋ก๋ฒํธ๋ฅผ ๋ฐ์์. ์ปดํฌ๋ํธ๋ ๋ฐ๋์ ๋ชจ๋ ์ต์์์ ์ ์ํ์ธ์.
Q4. ์น๊ตฌ์๊ฒ ์ค๋ช ํ๋ค๋ฉด? ๐ข
Q4. "React ์์ key ๊ฐ ๋ญ์ผ?" ๋ผ๋ ์ง๋ฌธ์ ๋ฐ์์ด์. ๋น์ ๋ฅผ ์จ์ ํ ๋ฌธ์ฅ์ผ๋ก ์ค๋ช ํด๋ด์.
(์ ๋ต์ ์์ด์. ๋ณธ์ธ๋ง์ ๋น์ ๋ก ์ค๋ช ํ ์ ์์ผ๋ฉด ์ถฉ๋ถํด์.)
์์ ๋ต๋ณ๋ค:
"
key๋ ์ปดํฌ๋ํธ์ ์ฃผ๋ฏผ๋ฑ๋ก๋ฒํธ์ผ. ๋ฒํธ๊ฐ ๋ฐ๋๋ฉด React ๋ ์ ์ฌ๋์ด ์จ ๊ฒ์ฒ๋ผ ์ํ๋ฅผ ๋ฆฌ์ ํ๊ณ , ๋ฒํธ๊ฐ ๊ฐ์ผ๋ฉด ์ด์ฌ๋ฅผ ๊ฐ๋ ๊ฐ์ ์ฌ๋์ผ๋ก ์ธ์ํด."
"
key๋ ํธํ ๋ฐฉ ๋ฒํธํ ๊ฐ์ ๊ฑฐ์ผ. ์๋์ ๊ฐ์๋ ๋ฐฉ ๋ฒํธํ์ ๋ฐ๊พธ๋ฉด ์ฒดํฌ์ธ ๊ธฐ๋ก(์ํ)์ด ์ด๊ธฐํ๋ผ์ ์์ ํ ์ ํฌ์๊ฐ์ผ๋ก ์ฒ๋ฆฌํด."
"
key๋ ๊ฒ์์ ์บ๋ฆญํฐ ID ์ผ. ์บ๋ฆญํฐ ์ข ๋ฅ(ํ์ )๊ฐ ๊ฐ์๋ ID ๊ฐ ๋ค๋ฅด๋ฉด ์์ดํ (์ํ)์ด ์๋ ์ ์บ๋ฆญํฐ๋ก ์์ํด."
๐ก ์ด ๋น์ ๋ฅผ ์ง์ ๋ง๋ค์ด๋ดค๋ค๋ฉด: ๊ฐ๋ ์ ์๋ฒฝํ ์ดํดํ ๊ฑฐ์์.
Q5. ์ฌํ ์ถ๋ก ๐ด
Q5. ์๋ ๋ ์ฝ๋์ ๋์์ด ์ด๋ป๊ฒ ๋ค๋ฅธ์ง ์ค๋ช ํด๋ณด์ธ์.
// ์ฝ๋ A
function App() {
const [count, setCount] = useState(0);
return (
<div>
{count % 2 === 0 ? <Counter /> : <Counter />}
<button onClick={() => setCount(c => c + 1)}>์ฆ๊ฐ</button>
</div>
);
}
// ์ฝ๋ B
function App() {
const [count, setCount] = useState(0);
return (
<div>
{count % 2 === 0
? <Counter key="even" />
: <Counter key="odd" />
}
<button onClick={() => setCount(c => c + 1)}>์ฆ๊ฐ</button>
</div>
);
}- A) ์ฝ๋ A, B ๋ชจ๋ ๋ฒํผ์ ๋๋ฅผ ๋๋ง๋ค Counter ์ํ๊ฐ ๋ฆฌ์ ๋๋ค.
- B) ์ฝ๋ A ๋ Counter ์ํ๊ฐ ์ ์ง๋๊ณ , ์ฝ๋ B ๋ ๋ฒํผ์ ๋๋ฅผ ๋๋ง๋ค Counter ์ํ๊ฐ ๋ฆฌ์ ๋๋ค.
- C) ์ฝ๋ A, B ๋ชจ๋ Counter ์ํ๊ฐ ์ ์ง๋๋ค. key ๋ ์ํฅ์ ์ฃผ์ง ์๋๋ค.
- D) ์ฝ๋ A ์์ Counter ์ํ๋ ์ ์ง๋์ง๋ง, ์ฝ๋ B ์์๋ key ๊ฐ ์ง์/ํ์๋ง๋ค ๋ฌ๋ผ์ง๋ฏ๋ก ๋งค๋ฒ ์ ์ธ์คํด์ค๊ฐ ๋ง์ดํธ๋๋ค.
โ ์ ๋ต: B โ ๋ ์ฝ๋์ ์ฐจ์ด๊ฐ ๋ฐ๋ก
key์ ํต์ฌ์ด์์.์ฝ๋ A ๋ถ์:
- ๊ฐ์ ์์น์ ํญ์
Counterํ์ (์ง์๋ ํ์๋ ๊ฐ์ ํ์ )- key ์์ โ React ๋ ๋์ผ ์ธ์คํด์ค๋ก ์ธ์
- ๋ฒํผ์ ๋๋ฌ๋ Counter ์ํ ์ ์ง
์ฝ๋ B ๋ถ์:
count๊ฐ ์ง์์ผ ๋:key="even"Countercount๊ฐ ํ์์ผ ๋:key="odd"Counter- key ๊ฐ "even" โ "odd" ๋ก ๋ฒ๊ฐ์ ๋ฐ๋ โ ๋งค ํด๋ฆญ๋ง๋ค ๊ธฐ์กด Counter ์ธ๋ง์ดํธ + ์ Counter ๋ง์ดํธ
- ๋ฒํผ์ ๋๋ฅผ ๋๋ง๋ค Counter ์ํ๊ฐ 0์ผ๋ก ๋ฆฌ์ ๋จ
์ค๋ต ํด์ค:
- A โ ์ฝ๋ A ๋ ๋ฆฌ์ ๋์ง ์์์. key ์์ด ๊ฐ์ ์์น, ๊ฐ์ ํ์ ์ด๋ฉด ์ํ ์ ์ง์์.
- C โ ์ฝ๋ B ์์ key ๋ ๊ฐ๋ ฅํ๊ฒ ์ํฅ์ ์ค์. key ๋ณ๊ฒฝ = ์ ์ธ์คํด์ค ๊ฐ์ ๋ง์ดํธ์์.
- D โ B ์ ์ค๋ช ์ ๋ง์ง๋ง A ์ ์ค๋ช ์ด ํ๋ ธ์ด์. ์ฝ๋ A ์์๋ ์ํ๊ฐ ์ ์ง๋ผ์.
๐ ํต์ฌ ๊ธฐ์ต๋ฒ: key ๊ฐ ์์ผ๋ฉด ์์น์ ํ์ ๋ง ๋ณด๊ณ ํ๋จํด์. key ๊ฐ ์์ผ๋ฉด key ๊น์ง ์ธ ๊ฐ์ง๋ฅผ ๋ชจ๋ ๋ด์. "๊ฐ์ ์์น + ๊ฐ์ ํ์ + ๊ฐ์ key" ๊ฐ ๋์ด์ผ ๋์ผ ์ธ์คํด์ค์์.
๐ ๋ ์์๋ณด๊ธฐ
์ด ๋ฌธ์์์ ๋ค๋ฃฌ ๋ด์ฉ๊ณผ ์ฐ๊ฒฐ๋ ๊ณต์ ๋ฌธ์ ๋ฐ ์ฌํ ํ์ต ์๋ฃ์์:
- React ๊ณต์ ๋ฌธ์ โ Preserving and Resetting State: React ๋ ๋ ํธ๋ฆฌ์ ์ํ ๋ณด์กด/๋ฆฌ์ ์ ๋ํ ๊ณต์ ์ค๋ช ์ด์์. ์ด ๋ฌธ์์ ํต์ฌ ์๋ฆฌ๊ฐ ์ฌ๊ธฐ์ ๋์์ด์.
- React ๊ณต์ ๋ฌธ์ โ Rendering Lists:
key์ ๊ณต์ ์ค๋ช ๊ณผ ์์ ์ ์ธ key ๊ฐ ์ ํ ๊ธฐ์ค์ ๋ค๋ค์. - Dan Abramov ์ "Why Do React Elements Have a
$$typeofProperty?": React ๊ฐ ์ปดํฌ๋ํธ ํ์ ์ ์ด๋ป๊ฒ ๋น๊ตํ๋์ง ๋ด๋ถ ๊ตฌํ์ ๊น๊ฒ ๋ค๋ค์. - Kent C. Dodds ์ "Understanding React's key prop":
key์ ๋์ ์๋ฆฌ๋ฅผ ์ค์ฉ์ ์ธ ์์ ๋ก ์ค๋ช ํด์. ์ด ๋ฌธ์์ ํจํด๋ค์ ํฐ ์๊ฐ์ ์คฌ์ด์.
๋ค์์ ์ฝ์ผ๋ฉด ์ข์ ๋ฌธ์:
์ด ๋ฌธ์์์ ๋ฐฐ์ด ๋ ๋ ํธ๋ฆฌ์ ์ํ ์์น ๊ฐ๋ ์ React ์ต์ ํ์ ๊ธฐ๋ฐ์ด ๋ผ์. ๋ค์ ์ฃผ์ ๋ค๊ณผ ์์ฐ์ค๋ฝ๊ฒ ์ฐ๊ฒฐ๋ผ์:
React.memo์useMemo: ๋ ๋ ํธ๋ฆฌ์ ์ด๋ ์์น์์ ๋ฆฌ๋ ๋๋ง์ ์ฐจ๋จํ ์ง ๊ฒฐ์ ํ๋ ๋๊ตฌ์์.useReducer: ๋ณต์กํ ์ํ ๋ก์ง์ ์ปดํฌ๋ํธ ์ธ๋ถ๋ก ๋ถ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ด์์. ์ํ๊ฐ ๋ ๋ ํธ๋ฆฌ ์์น์ ์ฐ๊ฒฐ๋๋ค๋ ์๋ฆฌ๋ ๋์ผํด์.- Context ์ ์ํ ๋์ด์ฌ๋ฆฌ๊ธฐ(State Lifting): ์ฌ๋ฌ ์ปดํฌ๋ํธ๊ฐ ๊ฐ์ ์ํ๋ฅผ ๊ณต์ ํด์ผ ํ ๋ ์ํ๋ฅผ ์ด๋ ํธ๋ฆฌ ์์น์ ์ฌ๋ ค์ผ ํ๋์ง ํ๋จํ๋ ๊ธฐ์ค์ด ๋ผ์.