๐ŸŽจ 03. ๋ถˆ๋ณ€์„ฑ(Immutability)๊ณผ ๋ Œ๋” ํŠธ๋ฆฌ๊ฑฐ โ€” ํ™”๋ฉด์ด ์™œ ์•ˆ ๋ณ€ํ•˜์ฃ ?

๐Ÿ“‹ ๊ฐœ์š”

๊ฐ์ฒด์™€ ๋ฐฐ์—ด ์ƒํƒœ๋ฅผ ๋‹ค๋ฃฐ ๋•Œ ์™œ ์Šคํ”„๋ ˆ๋“œ ๋ฌธ๋ฒ•(...)์ด ํ•„์š”ํ•œ์ง€, ๋ถˆ๋ณ€์„ฑ์„ ์ง€ํ‚ค์ง€ ์•Š์œผ๋ฉด ์ผ์–ด๋‚˜๋Š” ์ฐธ์‚ฌ๋ฅผ ๋ฐฐ์›๋‹ˆ๋‹ค.

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • ๋ฆฌ์•กํŠธ๊ฐ€ ๋ Œ๋”๋ง์„ ์‹œ์ž‘ํ• ์ง€ ๋ง์ง€ ๊ฒฐ์ •ํ•˜๋Š” ๊ธฐ์ค€(Object.is ๋™๋“ฑ์„ฑ ๋น„๊ต)์„ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ƒํƒœ์˜ ๋ฐฐ์—ด์ด๋‚˜ ๊ฐ์ฒด์— ์›๋ณธ์„ ํ›ผ์†(Mutate)ํ–ˆ์„ ๋•Œ ๋ฒŒ์–ด์ง€๋Š” '์นจ๋ฌต์˜ ๋ฒ„๊ทธ' ์›์ธ์„ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๊นŠ์ด๊ฐ€ ๊นŠ์€ ๊ฐ์ฒด๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์–•์€ ๋ณต์‚ฌ(Shallow Copy)ํ•˜์—ฌ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฒ•์„ ๊ฟฐ๋šซ๋Š”๋‹ค.

๐Ÿ“‹ ๋ชฉ์ฐจ


๐Ÿ“Œ ์ด ๋ฌธ์„œ๋ฅผ ์ฝ๊ธฐ ์ „์—

โฑ๏ธ ์˜ˆ์ƒ ์ฝ๊ธฐ ์‹œ๊ฐ„: 15๋ถ„ / ํ•ต์‹ฌ ํŒŒํŠธ: 8๋ถ„

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ๋ฐฐ๊ฒฝ ์„ธ๊ณ„๊ด€: '์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ'

  • ์˜์ฒ (์‹ ์ž…): "์–ด๋ผ? ์œ ์ € ํ”„๋กœํ•„ ํŽ˜์ด์ง€์—์„œ user.nickname = '์ƒˆ์ด๋ฆ„' ์œผ๋กœ ์ง์ ‘ ๋ฌธ์ž์—ด์„ ๋ฐ”๊ฟจ๋Š”๋ฐ ํ™”๋ฉด์ด ์•ˆ ๋ณ€ํ•ด์š”. ๋กœ๊ทธ ์ฐ์–ด๋ณด๋ฉด ๋ถ„๋ช…ํžˆ ๊ฐ์ฒด ๊ฐ’์€ ์ž˜ ๋“ค์–ด๊ฐ€ ์žˆ๋Š”๋ฐ ๋ง์ด์ฃ ! ๋ฆฌ์•กํŠธ ๋ฒ„๊ทธ ์•„๋‹Œ๊ฐ€์š”?"
  • ์˜ํ˜ธ(๋ฆฌ๋“œ): "์˜์ฒ  ๋‹˜... ๋ฆฌ์•กํŠธ๋Š” ๋ฐ”๋น ์„œ ๊ฐ์ฒด ์•ˆ์˜ ๋‚ด์šฉ๋ฌผ์„ ์ผ์ผ์ด ๊ฒ€์‚ฌํ•˜์ง€ ์•Š์•„์š”. ๊ฐ์ฒด์˜ '๊ป๋ฐ๊ธฐ ์ฃผ์†Œ'๊ฐ€ ๊ทธ๋Œ€๋กœ๋‹ˆ๊นŒ ์•ˆ ๋ฐ”๋€ ์ค„ ์•Œ๊ณ  ๋ Œ๋”๋ง์„ ์ฟจํ•˜๊ฒŒ ๊ฑด๋„ˆ๋›ด ๊ฑฐ์ž–์•„์š”!"

๐Ÿค” ์™œ ์•Œ์•„์•ผ ํ•˜๋Š”๊ฐ€: ์นจ๋ฌตํ•˜๋Š” ๋ฆฌ์•กํŠธ์˜ ํ•จ์ •

๋‹จ์ˆœํ•œ ์ˆซ์ž๋‚˜ ๋ฌธ์ž๋Š” ๊ฐ’์ด ๋ฐ”๋€Œ๋ฉด ์ž˜๋งŒ ๋ Œ๋”๋ง๋˜๋˜ ๋ฆฌ์•กํŠธ๊ฐ€, ์œ ๋… ๊ฐ์ฒด(Object)๋‚˜ ๋ฐฐ์—ด(Array) ์ƒํƒœ๋ฅผ ๋‹ค๋ฃฐ ๋•Œ๋ฉด ์•„๋ฌด๋Ÿฐ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ํ•˜๋‚˜ ์—†์ด "ํ™”๋ฉด๋งŒ" ์—…๋ฐ์ดํŠธ์— ์‹คํŒจํ•˜๋Š” ๊ธฐ๊ดดํ•œ ํ˜„์ƒ์„ ๊ฒช์–ด๋ดค์„ ๊ฑฐ์•ผ.

๐Ÿค” ์ž ๊น, ๋จผ์ € ์ƒ๊ฐํ•ด๋ด
์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์ฝ”๋“œ์•ผ. ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ํ™”๋ฉด์˜ ๋‹‰๋„ค์ž„์ด ๋ฐ”๋€”๊นŒ?

// โŒ ์ˆœ์ง„ํ•œ ์ฝ”๋“œ (Naive Approach)
function UserProfile() {
  const [user, setUser] = useState({ id: 1, name: '์ดˆ๋ณด์˜์ฒ ' });
 
  const handleChangeName = () => {
    user.name = '๋งˆ์Šคํ„ฐ์˜์ฒ ';  // ์›๋ณธ ๊ฐ์ฒด ์ง์ ‘ ์ˆ˜์ • (Mutation)
    setUser(user);        // ๋ณ€๊ฒฝ๋œ ์ƒํƒœ ์„ธํŒ…!
  };
 
  return (
    <div>
      <p>์œ ์ € ์ด๋ฆ„: {user.name}</p>
      <button onClick={handleChangeName}>์ด๋ฆ„ ๋ณ€๊ฒฝ!</button>
    </div>
  );
}

์˜์ฒ ์ด์˜ ์˜ˆ์ƒ๊ณผ ๋‹ค๋ฅด๊ฒŒ ํ™”๋ฉด์€ ์ ˆ๋Œ€ ๋งˆ์Šคํ„ฐ์˜์ฒ ๋กœ ์—…๋ฐ์ดํŠธ๋˜์ง€ ์•Š์•„. ๋งˆ์น˜ ์–ผ์–ด๋ถ™์€ ๊ฒƒ์ฒ˜๋Ÿผ.
๋Œ€์ฒด ๋ฆฌ์•กํŠธ ์•ˆ์—์„œ ๋ฌด์Šจ ์ผ์ด ์ผ์–ด๋‚œ ๊ฑธ๊นŒ?


๐Ÿ—๏ธ ๋น„์œ ๋กœ ๋จผ์ € ์ดํ•ดํ•˜๊ธฐ

๐Ÿง’ 5์‚ด์—๊ฒŒ ์„ค๋ช…ํ•œ๋‹ค๋ฉด?
์—„๋งˆ(๋ฆฌ์•กํŠธ)๊ฐ€ ์Œ๋‘ฅ์ด ์•„๊ธฐ ๋‘ ๋ช…์„ ๋Œ๋ณด๊ณ  ์žˆ์–ด. ์—„๋งˆ๋Š” ๋„ˆ๋ฌด ๋ฐ”๋น ์„œ ์•„๊ธฐ ์–ผ๊ตด์„ ์ž์„ธํžˆ ๋ณด์ง€ ์•Š๊ณ , ์˜ค์ง ์•„๊ธฐ๋ฐฉ ๋ฌธ ์•ž์— ๋ถ™์€ ๋ช…ํŒจ(๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ) ๋งŒ ์Šฌ์ฉ ๋ณด๊ณ  ์ง€๋‚˜๊ฐ€๋Š” ๋ฒ„๋ฆ‡์ด ์žˆ์–ด.
๋„ค๊ฐ€ ๋ฐฉ์— ๋“ค์–ด๊ฐ€์„œ ์•„๊ธฐ ํ•œ ๋ช…์˜ ๋นจ๊ฐ„ ๋ชจ์ž๋ฅผ ํŒŒ๋ž€์ƒ‰์œผ๋กœ ๋ชฐ๋ž˜ ๋ฐ”๊ฟ”์”Œ์› ์–ด(user.name = '๋งˆ์Šคํ„ฐ์˜์ฒ ').
๊ทธ๋ฆฌ๊ณ  ์—„๋งˆํ•œํ…Œ "์ €๊ธฐ ์ € ์•„๊ธฐ ํ™•์ธํ•ด๋ด์š”!"(setUser(user)) ๋ผ๊ณ  ๋ถˆ๋ €์ง€๋งŒ, ์—„๋งˆ๋Š” ๋ฌธํŒจ๋งŒ ๋ด…๋‹ˆ๋‹ค.
"์–ด? ๋ฌธํŒจ ์ด๋ฆ„์ด ๋˜‘๊ฐ™๋„ค? ์•ˆ ๋ฐ”๋€ ๊ฑฐ ๋‹ค ์•„๋‹ˆ๊นŒ ๊ท€์ฐฎ๊ฒŒ ํ•˜์ง€ ๋งˆ." ํ•˜๊ณ  ๊ทธ๋ƒฅ ๊ฐ€๋ฒ„๋ฆฌ๋Š” ๊ฑฐ์ง€. (์ƒˆ ์ง‘์œผ๋กœ ์ด์‚ฌ ๊ฐ€์ง€ ์•Š๋Š” ์ด์ƒ ๋ฌด์‹œํ•จ)

โœ… ํ•ต์‹ฌ ์›๋ฆฌ:
๋ฆฌ์•กํŠธ๋Š” ์„ฑ๋Šฅ์„ ์•„๋ผ๊ธฐ ์œ„ํ•ด ๊ทน๋‹จ์ ์ธ ํšจ์œจ์„ ์ถ”๊ตฌํ•ด. ์ƒํƒœ๊ฐ€ ๋ฐ”๋€Œ์—ˆ๋Š”์ง€ ํŒ๋‹จํ•  ๋•Œ ๊ฐ์ฒด ๋‚ด๋ถ€์˜ ์†์„ฑ์„ 1์ฐจ์›๋ถ€ํ„ฐ ๋๊นŒ์ง€ ์‹น ๋‹ค ๊นŒ์„œ ๋น„๊ตํ•˜๋Š” ๋ฌด๊ฑฐ์šด ์ง“(๊นŠ์€ ๋น„๊ต)์„ ํ•˜์ง€ ์•Š์•„.
์˜ค์ง ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ Object.is() (์‰ฝ๊ฒŒ ๋งํ•ด === ์ผ์น˜ ์—ฐ์‚ฐ์ž)๋ฅผ ํ†ตํ•ด ๊ป๋ฐ๊ธฐ์˜ ๋ฉ”๋ชจ๋ฆฌ ์ฐธ์กฐ ์ฃผ์†Œ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜์—ˆ๋Š”์ง€๋งŒ 0.001์ดˆ ๋งŒ์— ๋น„๊ตํ•ด ๋ฒ„๋ฆฌ์ง€. ๊ฐ์ฒด ๋‚ด๋ถ€๋ฅผ ๋ฐฑ๋‚  ์‘ค์‹œ๊ณ  ๋œฏ์–ด ๊ณ ์ณ๋ดค์ž, ๊ป๋ฐ๊ธฐ ์ฃผ์†Œ๊ฐ€ ๊ฐ™์œผ๋ฉด ๋ฆฌ์•กํŠธ๋Š” ์ ˆ๋Œ€ ํ™”๋ฉด์„ ๋‹ค์‹œ ๊ทธ๋ฆฌ์ง€ ์•Š์•„. ์ด๋ฅผ ๋ถˆ๋ณ€์„ฑ(Immutability) ์„ ์ง€ํ‚ค์ง€ ์•Š์€ ๊ฒฝ์šฐ๋ผ๊ณ  ๋ถˆ๋Ÿฌ.


๐Ÿงฉ ๋ถˆ๋ณ€์„ฑ(Immutability)์„ ์ง€ํ‚ค๋Š” ์–•์€ ๋ณต์‚ฌ์˜ ์šฐ์•„ํ•จ

โŒ ์˜์ฒ ์ด์˜ ๋ฐฐ์—ด ํญํŒŒ ์‚ฌ๊ณ 

๊ฒŒ์‹œ๊ธ€์— ๋‹ฌ๋ฆฐ ๋Œ“๊ธ€(๋ฐฐ์—ด)๋“ค์„ ๋‹ค๋ฃฐ ๋•Œ๋„ ๋˜‘๊ฐ™์€ ์ผ์ด ๋ฒŒ์–ด์ ธ. ๋ฐฐ์—ด์— ๋Œ“๊ธ€์„ ์ถ”๊ฐ€ํ–ˆ๋Š”๋ฐ ํ™”๋ฉด์— ์ƒˆ ๋Œ“๊ธ€์ด ์•ˆ ๋– .

// โŒ ์˜์ฒ ์ด์˜ Naive ๋ฐฐ์—ด ์—…๋ฐ์ดํŠธ
const [comments, setComments] = useState(['์žฌ๋ฐŒ๋„ค์š”', 'ํผ๊ฐ€์š”']);
 
const handleAddComment = () => {
  comments.push('๋ฐ˜์‚ฌ์š”~'); // ๋ฐฐ์—ด ์›๋ณธ ํ›ผ์† (Mutation)
  setComments(comments);  // ์ฃผ์†Œ๊ฐ’์ด ๊ทธ๋Œ€๋กœ์ด๋ฏ€๋กœ ๋ Œ๋”๋ง ์”นํž˜!
};

push, pop, reverse, splice ๊ฐ™์€ ์• ๋“ค์€ ๊ธฐ์กด ๋ฉ”๋ชจ๋ฆฌ ํ†ต(๋ฐฐ์—ด ์›๋ณธ)์„ ๋ฐ•์‚ด ๋‚ด๊ณ  ๋‚ด๋ถ€๋ฅผ ๋ฐ”๊พธ๋Š” ์•…๋‹น๋“ค์ด์•ผ. ๋ฆฌ์•กํŠธ์—์„œ ์ด๋Ÿฐ ์• ๋“ค์„ ํ˜์˜คํ•ด.

โœ… ์˜ํ˜ธ์˜ ๋ฆฌํŒฉํ† ๋ง: Spread ์—ฐ์‚ฐ์ž ๋˜์ง€๊ธฐ

๋ถ€์„œ์ง„ ์›๋ณธ์„ ๊ณ ์น˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ, ์•„์˜ˆ ๊น”๋”ํ•œ ์ƒˆ ์ง‘(์ƒˆ๋กœ์šด ์ฐธ์กฐ ์ฃผ์†Œ) ์„ ์‚ฌ์„œ ๊ธฐ์กด ์ง๋“ค์„ ๋ณต๋ถ™ํ•ด ์˜ค๋Š” ์ „๋žต์ด ํ•„์ˆ˜์•ผ. ์ด๊ฒŒ ๊ทธ ์œ ๋ช…ํ•œ ๋งˆ์นจํ‘œ ์„ธ ๊ฐœ ... (์Šคํ”„๋ ˆ๋“œ ๋ฌธ๋ฒ•)์˜ ์กด์žฌ ์ด์œ ์ง€.

// โœ… ์šฐ์•„ํ•œ ์ฝ”๋“œ (Pro Approach)
const handleAddComment = () => {
  // ์ƒˆ๋กœ์šด ๋ฐฐ์—ด ๊ป๋ฐ๊ธฐ []๋ฅผ ๋งŒ๋“ค๊ณ , ๊ธฐ์กด ์ง(...comments)์„ ํ’€๊ณ , ๋งจ ๋’ค์— ์ƒˆ ๋Œ“๊ธ€์„ ์ถ”๊ฐ€ํ•œ๋‹ค.
  setComments([...comments, '๋ฐ˜์‚ฌ์š”~']); 
};

์ƒˆ๋กœ์šด []๊ฐ€ ์„ ์–ธ๋˜๋Š” ์ˆœ๊ฐ„ ์™„์ „ํžˆ ์ƒˆ๋กœ์šด ๋ฉ”๋ชจ๋ฆฌ ๋ฌธํŒจ๊ฐ€ ํƒ„์ƒํ•˜๊ณ , ๋ฆฌ์•กํŠธ๋Š” "์•—! ๋ฌธํŒจ๊ฐ€ ๋‹ฌ๋ผ์กŒ๋„ค! ๋ฆฌ๋ Œ๋”๋ง ํ•ด์•ผ์ง€!" ํ•˜๊ณ  ๊ธฐ๋ถ„ ์ข‹๊ฒŒ ์›€์ง์ด๊ฒŒ ๋ผ.


๐Ÿ’ฅ ์—๋Ÿฌ ํ•ด๊ฒฐ ์นดํƒˆ๋กœ๊ทธ

โŒ ์ค‘์ฒฉ ๊ฐ์ฒด(Nested Object)์˜ ๋ถ€๋ถ„ ๊ฐฑ์‹  ๋ˆ„๋ฝ ๋ฒ„๊ทธ

์–ธ์ œ ๋‚˜์˜ค๋Š”๊ฐ€?

const [user, setUser] = useState({
  name: '์˜์ฒ ',
  address: { city: 'Seoul', zip: '123' },
});
 
const handleCityChange = () => {
  setUser({
    ...user, // ์–•์€ ๋ณต์‚ฌ
    address: { city: 'Busan' }, // ๐Ÿ’ฅ zip ๋ฐ์ดํ„ฐ๊ฐ€ ๋‚ ์•„๊ฐ€๋ฒ„๋ฆผ!
  });
};

์›์ธ: ์Šคํ”„๋ ˆ๋“œ ์—ฐ์‚ฐ์ž ...๋Š” ๊ป๋ฐ๊ธฐ ํ•œ ๊บผํ’€๋งŒ '์ƒˆ ์ง‘'์œผ๋กœ ๋ณต์‚ฌํ•ด ์ฃผ๋Š” ์–•์€ ๋ณต์‚ฌ(Shallow Copy) ๋„๊ตฌ์•ผ. address๋ผ๋Š” ์•ˆ๋ฐฉ ๋ฌธ์ง๊นŒ์ง€ ์ƒˆ๊ฑธ๋กœ ๊ฐˆ์•„๋ผ์›Œ ์ฃผ์ง€๋Š” ์•Š์•„. ๊ทธ๋ž˜์„œ ๋ฉ์น˜ ํฐ JSON ๋ฉ์–ด๋ฆฌ์˜ ๊นŠ์ˆ™ํ•œ ๊ณณ์„ ๋ฐ”๊ฟ€ ๋•Œ ๋จธ๋ฆฌ๊ฐ€ ํ„ฐ์ ธ๋‚˜๊ฐ€๊ฒŒ ๋˜์ง€. ๋งค๋ฒˆ { ...user, address: { ...user.address, city: 'Busan' } } ์ฒ˜๋Ÿผ ๋๋„ ์—†์ด ๋ณต์‚ฌ ๊ป์งˆ์„ ๊นŒ์•ผ ํ•˜๋‹ˆ๊นŒ ์œ ์ง€๋ณด์ˆ˜ ์ง€์˜ฅ(Maintenance Nightmare)์ด ํŽผ์ณ์ ธ.

ํ•ด๊ฒฐ์ฑ…:
์ด๋Ÿด ๋• ์Šคํ”„๋ ˆ๋“œ๋งŒ ๋ฏฟ์ง€ ๋ง๊ณ  ๋ฆฌ์•กํŠธ ์ƒํƒœ๊ณ„์˜ ๊ตฌ์›์ž, Immer ๊ฐ™์€ ๋ถˆ๋ณ€์„ฑ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋„์ž…ํ•˜๋Š” ๊ฒŒ ํ”„๋กœ๋“ค์˜ ์ •์„์ด์•ผ.

import { produce } from "immer";
 
const handleCityChange = () => {
  setUser(produce((draft) => {
    // ๋งˆ์น˜ 'mutatation(์ง์ ‘ ์ˆ˜์ •)' ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์งœ๋ฉด, Immer๊ฐ€ ์•Œ์•„์„œ ๋ณต์‚ฌ๋ณธ์„ ๋ฆฌํ„ดํ•ด์ค˜!
    draft.address.city = 'Busan'; 
  }));
}

๐Ÿ ์ด๋ฒˆ์— ๋ฐฐ์šด ๋‚ด์šฉ ์ด์ •๋ฆฌ

์ƒํ™ฉโŒ ๋‚˜์œ ์˜ˆ (Mutation ์œ ๋ฐœ)โœ… ์ข‹์€ ์˜ˆ (Immutability ์œ ์ง€)
๊ฐ์ฒด ์—…๋ฐ์ดํŠธuser.name = '๊น€'; setUser(user)setUser({ ...user, name: '๊น€' })
๋ฐฐ์—ด ํ•ญ๋ชฉ ์ถ”๊ฐ€arr.push(newObj); setArr(arr);setArr([...arr, newObj]);
๋ฐฐ์—ด ํ•ญ๋ชฉ ์‚ญ์ œ์›๋ณธ ๋ฐฐ์—ด splice ์ œ๊ฑฐsetArr(arr.filter(item => item.id !== 1))
๋ณต์žกํ•œ ๋ށ์Šค ์ˆ˜์ •๋์žฅ๋‚˜๋Š” ์ค‘์ฒฉ ์Šคํ”„๋ ˆ๋“œ ...a, { ...b }Immer ๋„์ž…ํ•˜์—ฌ draft ์ง๊ด€์  ์ˆ˜์ •

๐Ÿ’ก ํ•œ ์ค„๋กœ ๊ธฐ์–ตํ•˜๊ธฐ
๋ฆฌ์•กํŠธ์˜ ๋ˆˆ์€ ํ๋ฆฟํ•˜๋‹ค. ์ง‘(๊ฐ์ฒด) ๋‚ด๋ถ€์— ์žˆ๋Š” ์†ŒํŒŒ ์‹œํŠธ ์ƒ‰๊น”์ด ๋ฐ”๋€Œ์—ˆ๋‹ค๊ณ  ์•Œ์•„์ฑ„์ง€ ๋ชปํ•œ๋‹ค. ์ƒˆ๋กœ์šด ์ง‘์„ ํ†ต์งธ๋กœ ์ด์‚ฌ(... ์‚ฌ์šฉ)ํ•ด์•ผ๋งŒ ๋น„๋กœ์†Œ ๋ˆˆ์„ ๋น„๋น„๋ฉฐ ๋ Œ๋”๋ง์— ๋Œ์ž…ํ•œ๋‹ค.


๐Ÿฃ ์˜์ฒ ์ด์˜ ํ‡ด๊ทผ ์ผ๊ธฐ

๋ฐฐ์—ด์— push ํ•˜๊ณ  ์™œ ํ™”๋ฉด ์•ˆ ๋ฐ”๋€Œ์ง€ ํ•˜๋ฉด์„œ ์‚ฝ์งˆํ–ˆ๋˜ ์–ด์ œ์˜ ๋‚˜... ๋ฆฌ์•กํŠธ๊ฐ€ ๊ฐ์ฒด ์†๊นŒ์ง€ ์ผ์ผ์ด ๊นŒ๋ณด์ง€ ์•Š๊ณ  ๊ป๋ฐ๊ธฐ(๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ)๋งŒ ๋ณธ๋‹ค๋Š” ์‚ฌ์‹ค์„ ๊นจ๋‹ซ๊ณ  ๋‚˜๋‹ˆ ๋ Œ๋”๋ง ํŠธ๋ฆฌ๊ฑฐ ๋งค์ปค๋‹ˆ์ฆ˜์ด ํ™• ์ดํ•ด๋๋‹ค. ์˜ํ˜ธ ๋‹˜์ด ์Œ๋‘ฅ์ด ์•„๊ธฐ ๋น„์œ ํ•ด์ฃผ์…จ์„ ๋•Œ ์ง„์งœ ๋ช…์พŒํ–ˆ๋‹ค.

๐Ÿ’ก "๋ฆฌ์•กํŠธ์˜ ๋ˆˆ์€ ํ๋ฆฟํ•˜๋‹ค! ๊ฐ์ฒด๋‚˜ ๋ฐฐ์—ด์„ ๊ฑด๋“œ๋ฆด ๋• ์›๋ณธ์„ ํ›ผ์†ํ•˜์ง€ ๋ง๊ณ  ๋ฌด์กฐ๊ฑด ์–•์€ ๋ณต์‚ฌ(... ์Šคํ”„๋ ˆ๋“œ ์—ฐ์‚ฐ์ž)๋กœ ์ƒˆ ์ง‘์„ ํŒŒ์ฃผ์ž."

์•ž์œผ๋กœ๋Š” ๊นŠ์€ ๋ณต์‚ฌ ํ•˜๋А๋ผ ์ค‘์ฒฉ ์Šคํ”„๋ ˆ๋“œ๋กœ ๊ณ ํ†ต๋ฐ›์ง€ ๋ง๊ณ , ๋‚ด์ผ ๋‹น์žฅ ํŒ€ ํ”„๋กœ์ ํŠธ์— Immer๋ฅผ ๋„์ž…ํ•˜์ž๊ณ  ๊ฑด์˜ํ•ด ๋ด์•ผ๊ฒ ๋‹ค. ์กฐ๊ธˆ์”ฉ ํ”„๋กœ ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž์˜ ์‹œ์•ผ๊ฐ€ ์ƒ๊ธฐ๋Š” ๊ฒƒ ๊ฐ™์•„ ๋ฟŒ๋“ฏํ•˜๋‹ค. ์–ผ๋ฅธ ํ—ฌ์Šค์žฅ ๊ฐ€์„œ ๋•€ ์ซ™ ๋นผ๊ณ  ๊ฟ€์ž  ์ž์•ผ์ง€.


๐Ÿ“ ๋งˆ๋ฌด๋ฆฌ ํ€ด์ฆˆ

Q1. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ๋ฐฐ์—ด ๋ฉ”์„œ๋“œ ์ค‘์—์„œ ๋ถˆ๋ณ€์„ฑ์„ ํ•ด์น˜๋Š” ์•…๋‹น(Mutating method)๊ณผ ์›๋ณธ์„ ๋ณด์กดํ•˜๋Š” ์ฒœ์‚ฌ(Non-mutating method)๊ฐ€ ์„ž์—ฌ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฆฌ์•กํŠธ ์ƒํƒœ์— ์ง์ ‘ ๋•Œ๋ ค๋ฐ•์œผ๋ฉด ์น˜๋ช…ํƒ€๋ฅผ ์ž…ํžˆ๋Š” ์•…๋‹น๋งŒ์„ ๋ฌถ์€ ๊ฒƒ์€?

  • A) splice(), map(), push()
  • B) filter(), map(), concat()
  • C) push(), pop(), reverse(), splice()
  • D) slice(), filter(), reduce()

โœ… ์ •๋‹ต: C

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค: ๋ฆฌ์•กํŠธ ๋ฐฐ์—ด ์ƒํƒœ๋ฅผ ๊ฑด๋“œ๋ฆด ๋•Œ B๋‚˜ D์˜ ๋ฉ”์„œ๋“œ๋“ค(map, filter, concat, slice ๋“ฑ)์€ ํ•จ์ˆ˜ ์‹คํ–‰ ๊ฒฐ๊ณผ๋กœ "์™„์ „ํžˆ ์ƒˆ๋กœ์šด ๋ฐฐ์—ด"์„ ๋ฆฌํ„ดํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์•ˆ์ „ํ•ฉ๋‹ˆ๋‹ค(์ฒœ์‚ฌ). ํ•˜์ง€๋งŒ C์— ๋‚˜์—ด๋œ ๋ฉ”์„œ๋“œ๋“ค์€ ๋ฉ”๋ชจ๋ฆฌ์— ์ด๋ฏธ ํ• ๋‹น๋œ ์›๋ณธ ๋ฐฐ์—ด ์ž์ฒด์˜ ๋‚ด์šฉ์„ ์ฐข์–ด๋ฐœ๊ธฐ๊ฑฐ๋‚˜ ๋ง๋Œ„ ํ›„ ๊ธฐ์กด ์ฃผ์†Œ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค(์•…๋‹น). ๊ทธ๋ž˜์„œ ๋ฆฌ์•กํŠธ๊ฐ€ ๋ณ€ํ™”๋ฅผ ๊ฐ์ง€ํ•˜์ง€ ๋ชปํ•˜๊ณ  ๋ Œ๋”๋ง์„ ๋ฉˆ์ถ”๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

Q2. ๋‘ ๊ฐœ์˜ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด์˜ ์ผ์น˜ ์—ฌ๋ถ€์— ๋Œ€ํ•œ ์งˆ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ฝ”๋“œ ์‹คํ–‰ ๊ฒฐ๊ณผ๋ฅผ ๋งžํ˜€๋ณด์„ธ์š”.

const obj1 = { name: "์˜ํ˜ธ" };
const obj2 = { name: "์˜ํ˜ธ" };
console.log(Object.is(obj1, obj2)); // ๋˜๋Š” obj1 === obj2
  • A) true (๋‚ด๋ถ€ ๋‚ด์šฉ๋ฌผ์ด ์™„๋ฒฝํžˆ ๊ฐ™์œผ๋ฏ€๋กœ)
  • B) false (๋ฉ”๋ชจ๋ฆฌ์— ํ• ๋‹น๋œ ๊ณ ์œ  ์ฐธ์กฐ ์ฃผ์†Œ๊ฐ’์ด ์—„์—ฐํžˆ ๋‹ค๋ฅธ ๋‘ ๊ฐœ์˜ ์ง‘์ด๋ฏ€๋กœ)
  • C) undefined (์•Œ ์ˆ˜ ์—†์Œ)

โœ… ์ •๋‹ต: B

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค: ์ด๊ฒŒ ๋ฆฌ์•กํŠธ๊ฐ€ ๊นŠ์€ ๋ Œ๋”๋ง ๊ฐ์ง€๋ฅผ ํฌ๊ธฐํ•˜๊ณ  ์–•์€ ๋น„๊ต(===)๋ฅผ ํ•˜๋Š” ์ด์œ ์ž…๋‹ˆ๋‹ค. ๋ชจ์–‘๊ณผ ์†์„ฑ์ด 100% ๋˜‘๊ฐ™๋”๋ผ๋„ ์ปดํ“จํ„ฐ์˜ ํž™(Heap) ๊ณต๊ฐ„์— ๋”ฐ๋กœ ๋ฐฉ์„ ํŒ ๊ธฐ ๋•Œ๋ฌธ์— ์ฃผ์†Œ๊ฐ€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ์—ญ์œผ๋กœ ๋งํ•˜๋ฉด, let a = { id: 1 }; let b = a; b.id = 2;๋ผ๊ณ  ํ–ˆ์„ ๋•Œ a === b๋Š” ๋ฌด์กฐ๊ฑด true๊ฐ€ ๋‚˜์˜ต๋‹ˆ๋‹ค. ๊ฐ™์€ ๋ฐฉ์ด๋‹ˆ๊นŒ์š”! ๋ฆฌ์•กํŠธ๊ฐ€ ๊ป๋ฐ๊ธฐ๋งŒ ์ณ๋‹ค๋ณผ ๋•Œ ์ด๋ฅผ ํ—ท๊ฐˆ๋ฆฌ๊ฒŒ ํ•˜๋ฉด ๋ฐฉ๊ธˆ ์˜์ฒ ์ด ๊ฐ™์€ ๋Œ€์ฐธ์‚ฌ๊ฐ€ ์ผ์–ด๋‚ฉ๋‹ˆ๋‹ค.

Q3. '๋ถˆ๋ณ€์„ฑ'์„ ์ง€ํ‚จ๋‹ต์‹œ๊ณ  ์•„๋ž˜์ฒ˜๋Ÿผ ๊ธด๊ธ‰ ์ˆ˜์ •์„ ๋„์ž…ํ–ˆ์ง€๋งŒ ์—ฌ์ „ํžˆ ๋ฌธ์ œ๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ๋ฌด์—‡์ด ์ž˜๋ชป๋˜์—ˆ๋Š”์ง€, ์™„๋ฒฝํ•œ ํ•ด๊ฒฐ์ฑ…๊ณผ ์›๋ฆฌ(์–•์€ ๋ณต์‚ฌ์˜ ํ•œ๊ณ„)๋ฅผ ์„ค๋ช…ํ•ด๋ณด์„ธ์š”.

const [serverConfig, setConfig] = useState({ 
  port: 8080, 
  settings: { timeout: 3000 } 
});
 
const reduceTimeout = () => {
  const newConfig = { ...serverConfig }; // ์˜ค, ๋ถˆ๋ณ€์„ฑ ์ง€์ผฐ๋‹ค!
  newConfig.settings.timeout = 1000;     // ์ง์ ‘ ์ˆ˜์ •?
  setConfig(newConfig);
};

โœ… ์ •๋‹ต ๋ฐ ์ฃผ๊ด€์‹ ํ•ด์„ค:

์›์ธ(Why): ๋†€๋ž๊ฒŒ๋„ ๋ถ€๋ถ„์ ์ธ ์น˜๋ช…ํƒ€๋ฅผ ์ž…์Šต๋‹ˆ๋‹ค! newConfig = { ...serverConfig }๋Š” ์–•์€ ๋ณต์‚ฌ(Shallow Copy)๋กœ, ๋ฐ”๊นฅ์ชฝ ๊ป๋ฐ๊ธฐ(serverConfig ์ž์ฒด)์˜ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๋Š” ์ƒˆ๊ฒƒ์œผ๋กœ ๋ฐ”๋€Œ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋ฆฌ์•กํŠธ๋Š” "์•—! ๋ฐ”๋€Œ์—ˆ๋„ค?" ํ•˜๊ณ  ๋ Œ๋”๋ง์— ๋Œ์ž…ํ•˜๊ธด ํ•ฉ๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒ! ์–•์€ ๋ณต์‚ฌ ํŠน์„ฑ์ƒ ๋‚ด๋ถ€์— ์žˆ๋Š” ์ž์‹ ๊ฐ์ฒด settings์˜ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๋Š” ์™„์ „ํžˆ ์˜›๋‚  ๊ฒƒ๊ณผ ๋™์ผํ•˜๊ฒŒ ๋ณต์‚ฌ๋˜์–ด ์—ฐ๊ฒฐ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์ž์‹ ์ปดํฌ๋„ŒํŠธ ์ค‘ ์ผ๋ถ€๊ฐ€ React.memo ๋“ฑ์œผ๋กœ ์ตœ์ ํ™”๋˜์–ด ์žˆ๋‹ค๋ฉด, ๊ฑ”๋„ค๋“ค์€ settings ์ฃผ์†Œ๊ฐ€ ์•ˆ ๋ฐ”๋€Œ์—ˆ์œผ๋ฏ€๋กœ ๋ Œ๋”๋ง์„ ๊ฑฐ๋ถ€ํ•˜๋Š” ํŒŒํŽธํ™” ๋ฒ„๊ทธ๊ฐ€ ์ƒ๊น๋‹ˆ๋‹ค.
ํ•ด๊ฒฐ ๋กœ์ง: ๋‚ด๋ถ€์˜ ์ค‘์ฒฉ ๊ฐ์ฒด๊นŒ์ง€ ์™„์ „ํžˆ ์ƒˆ๋กœ ๋ณต์‚ฌํ•ด ์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. setConfig({ ...serverConfig, settings: { ...serverConfig.settings, timeout: 1000 } }) ์ฒ˜๋Ÿผ ๊ฐ ๊นŠ์ด๋งˆ๋‹ค ๋…๋ฆฝ๋œ ์–•์€ ๋ณต์‚ฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ฑฐ๋‚˜, ์ œ์ผ ์† ํŽธํ•˜๊ฒŒ Immer ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด ๊น”๋”ํ•˜๊ฒŒ ์šฐํšŒํ•˜๋Š” ๊ฒŒ ์ •์„์ž…๋‹ˆ๋‹ค.