JavaScript์˜ ๋งˆ๋ฒ• ๐Ÿช„: ๋ถˆ๋ณ€์„ฑ๊ณผ ์˜์† ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋กœ ์ฝ”๋“œ์— ๋‚ ๊ฐœ ๋‹ฌ๊ธฐ

์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - JavaScript์˜ ๋งˆ๋ฒ• ๐Ÿช„: ๋ถˆ๋ณ€์„ฑ๊ณผ ์˜์† ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋กœ ์ฝ”๋“œ์— ๋‚ ๊ฐœ ๋‹ฌ๊ธฐ

 

 

2025๋…„ 3์›” ๊ธฐ์ค€ ์ตœ์‹  JavaScript ํŠธ๋ Œ๋“œ๋ฅผ ๋ฐ˜์˜ํ•œ ๊ฐ€์ด๋“œ

์•ˆ๋…•? ์˜ค๋Š˜์€ JavaScript ์„ธ๊ณ„์—์„œ ์ •๋ง ์ค‘์š”ํ•˜์ง€๋งŒ ์ข…์ข… ๊ฐ„๊ณผ๋˜๋Š” ๊ฐœ๋…์ธ ๋ถˆ๋ณ€์„ฑ(Immutability)๊ณผ ์˜์† ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ(Persistent Data Structures)์— ๋Œ€ํ•ด ํ•จ๊ป˜ ์•Œ์•„๋ณผ ๊ฑฐ์•ผ. ๐Ÿ˜Š

์ด ๊ฐœ๋…๋“ค์ด ์™œ ์ค‘์š”ํ•˜๋ƒ๊ณ ? ์ตœ์‹  ํ”„๋ก ํŠธ์—”๋“œ ํ”„๋ ˆ์ž„์›Œํฌ๋‚˜ ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์ด ๋ชจ๋‘ ์ด ์›์น™์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๊ณ  ์žˆ๊ฑฐ๋“ ! React, Redux, Vuex ๋“ฑ์„ ์‚ฌ์šฉํ•ด๋ดค๋‹ค๋ฉด ์ด๋ฏธ ๋ถˆ๋ณ€์„ฑ์˜ ์ค‘์š”์„ฑ์„ ์–ด๋ ดํ’‹์ด ๋Š๊ผˆ์„ ๊ฑฐ์•ผ. ํ•˜์ง€๋งŒ ์˜ค๋Š˜์€ ๊ทธ ๊ฐœ๋…์„ ๋” ๊นŠ์ด ํŒŒํ—ค์ณ๋ณผ ๊ฑฐ์•ผ. ๐Ÿ•ต๏ธโ€โ™‚๏ธ

ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์„ธ๊ณ„์—์„œ ์žฌ๋Šฅ์„ ๋ฐœํœ˜ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, ์ด๋Ÿฐ ํ•ต์‹ฌ ๊ฐœ๋…๋“ค์„ ์ œ๋Œ€๋กœ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ด. ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ๋‹ค์–‘ํ•œ ์žฌ๋Šฅ์„ ๊ฑฐ๋ž˜ํ•˜๋“ฏ, ์šฐ๋ฆฌ๋„ ์˜ค๋Š˜ ๋ถˆ๋ณ€์„ฑ์ด๋ผ๋Š” ์žฌ๋Šฅ์„ ์Šต๋“ํ•ด๋ณด์ž! ๐Ÿš€

๐Ÿ“š ๋ชฉ์ฐจ

  1. ๋ถˆ๋ณ€์„ฑ์ด๋ž€ ๋ฌด์—‡์ธ๊ฐ€? ๊ทธ๋ฆฌ๊ณ  ์™œ ์ค‘์š”ํ• ๊นŒ?
  2. JavaScript์—์„œ์˜ ๊ฐ€๋ณ€์„ฑ ๋ฌธ์ œ์ 
  3. ๋ถˆ๋ณ€์„ฑ ๊ตฌํ˜„ํ•˜๊ธฐ: ๊ธฐ๋ณธ ํ…Œํฌ๋‹‰
  4. ์˜์† ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ž€?
  5. Immer, Immutable.js ๋“ฑ ์ธ๊ธฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ดํŽด๋ณด๊ธฐ
  6. ์‹ค์ „ ์˜ˆ์ œ: ๋ถˆ๋ณ€์„ฑ์œผ๋กœ ์„ฑ๋Šฅ ์ตœ์ ํ™”ํ•˜๊ธฐ
  7. 2025๋…„ ์ตœ์‹  ํŠธ๋ Œ๋“œ์™€ ๋ฏธ๋ž˜ ์ „๋ง

1. ๋ถˆ๋ณ€์„ฑ์ด๋ž€ ๋ฌด์—‡์ธ๊ฐ€? ๊ทธ๋ฆฌ๊ณ  ์™œ ์ค‘์š”ํ• ๊นŒ? ๐Ÿค”

๋ถˆ๋ณ€์„ฑ(Immutability)์€ ํ•œ๋ฒˆ ์ƒ์„ฑ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฐœ๋…์ด์•ผ. ์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด, "ํ•œ๋ฒˆ ๋งŒ๋“ค์–ด์ง„ ๊ฒƒ์€ ๋ฐ”๊พธ์ง€ ์•Š๊ณ , ํ•„์š”ํ•˜๋ฉด ์ƒˆ๋กœ ๋งŒ๋“ ๋‹ค"๋Š” ์›์น™์ด์ง€. ๋งˆ์น˜ ๋„ˆ์˜ ์ผ๊ธฐ์žฅ์ฒ˜๋Ÿผ - ํ•œ๋ฒˆ ์“ด ๋‚ด์šฉ์„ ์ง€์šฐ๊ฐœ๋กœ ์ง€์šฐ๊ณ  ๋‹ค์‹œ ์“ฐ๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ, ์ƒˆ ํŽ˜์ด์ง€์— ์ƒˆ๋กœ์šด ๋‚ด์šฉ์„ ์“ฐ๋Š” ๊ฑฐ์•ผ. ๐Ÿ“

๐Ÿ”„ ๊ฐ€๋ณ€์„ฑ vs ๋ถˆ๋ณ€์„ฑ

๊ฐ€๋ณ€์„ฑ(Mutability):

- ๋ฐ์ดํ„ฐ๊ฐ€ ์ƒ์„ฑ๋œ ํ›„์—๋„ ๋‚ด์šฉ์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Œ
- ๊ฐ™์€ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๋ฅผ ๊ณ„์† ์‚ฌ์šฉ
- ์˜ˆ: ๋ฐฐ์—ด์˜ push(), pop(), ๊ฐ์ฒด ์†์„ฑ ์ง์ ‘ ์ˆ˜์ •

๋ถˆ๋ณ€์„ฑ(Immutability):

- ๋ฐ์ดํ„ฐ๊ฐ€ ์ƒ์„ฑ๋œ ํ›„์—๋Š” ๋‚ด์šฉ์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์Œ
- ๋ณ€๊ฒฝ์ด ํ•„์š”ํ•˜๋ฉด ์ƒˆ๋กœ์šด ๋ฉ”๋ชจ๋ฆฌ์— ๋ณต์‚ฌ๋ณธ ์ƒ์„ฑ
- ์˜ˆ: ๋ฌธ์ž์—ด ๋ฉ”์„œ๋“œ๋“ค, Array.map(), Array.filter()

์™œ ๋ถˆ๋ณ€์„ฑ์ด ์ค‘์š”ํ• ๊นŒ? ๐ŸŒŸ

  1. ์˜ˆ์ธก ๊ฐ€๋Šฅ์„ฑ: ๋ฐ์ดํ„ฐ๊ฐ€ ์–ธ์ œ ์–ด๋””์„œ ๋ณ€ํ• ์ง€ ๊ฑฑ์ •ํ•  ํ•„์š”๊ฐ€ ์—†์–ด. ํ•œ๋ฒˆ ๋งŒ๋“ค์–ด์ง„ ๋ฐ์ดํ„ฐ๋Š” ํ•ญ์ƒ ๊ฐ™์€ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๋‹ˆ๊นŒ!
  2. ๋””๋ฒ„๊น… ์šฉ์ด์„ฑ: ๋ฒ„๊ทธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ๋ฐ์ดํ„ฐ ๋ณ€ํ™”๋ฅผ ์ถ”์ ํ•˜๊ธฐ ์‰ฌ์›Œ์ ธ. "๋ˆ„๊ฐ€ ์ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”๊ฟจ์ง€?"๋ผ๋Š” ์งˆ๋ฌธ์ด ์‚ฌ๋ผ์ง€๋‹ˆ๊นŒ.
  3. ๋™์‹œ์„ฑ ์ฒ˜๋ฆฌ: ์—ฌ๋Ÿฌ ํ•จ์ˆ˜๋‚˜ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ๊ฐ™์€ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•ด๋„ ์ถฉ๋Œ์ด ์—†์–ด.
  4. ์‹œ๊ฐ„ ์—ฌํ–‰ ๋””๋ฒ„๊น…: ์ด์ „ ์ƒํƒœ๋กœ ์‰ฝ๊ฒŒ ๋Œ์•„๊ฐˆ ์ˆ˜ ์žˆ์–ด. Redux DevTools ๊ฐ™์€ ๋„๊ตฌ๊ฐ€ ์ด๋ฅผ ํ™œ์šฉํ•˜์ง€.
  5. ์ฐธ์กฐ ๋™๋“ฑ์„ฑ ์ตœ์ ํ™”: ๊ฐ์ฒด๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ๋Š”์ง€ ๋น ๋ฅด๊ฒŒ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด (๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๋งŒ ๋น„๊ตํ•˜๋ฉด ๋จ).

"๋ถˆ๋ณ€์„ฑ์€ ๋‹จ์ˆœํ•œ ์ฝ”๋”ฉ ์Šคํƒ€์ผ์ด ์•„๋‹ˆ๋ผ, ์†Œํ”„ํŠธ์›จ์–ด ์„ค๊ณ„์˜ ์ฒ ํ•™์ด๋‹ค."

- ์–ด๋–ค ํ˜„๋ช…ํ•œ ๊ฐœ๋ฐœ์ž ๐Ÿ˜‰

2. JavaScript์—์„œ์˜ ๊ฐ€๋ณ€์„ฑ ๋ฌธ์ œ์  ๐Ÿ˜ฑ

JavaScript๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ฐ€๋ณ€(mutable) ์–ธ์–ด์•ผ. ํŠนํžˆ ๊ฐ์ฒด์™€ ๋ฐฐ์—ด์€ ์ฐธ์กฐ ํƒ€์ž…์ด๋ผ์„œ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋ถ€์ž‘์šฉ(side effects)์„ ์ผ์œผํ‚ค๊ธฐ ์‰ฝ์ง€. ์•„๋ž˜ ์˜ˆ์ œ๋ฅผ ํ•œ๋ฒˆ ๋ณผ๊นŒ?

// ๊ฐ€๋ณ€์„ฑ์œผ๋กœ ์ธํ•œ ๋ฌธ์ œ ์˜ˆ์‹œ
const user = { name: '์ฒ ์ˆ˜', age: 25 };
const userCopy = user;  // ์ฐธ์กฐ๋งŒ ๋ณต์‚ฌ๋จ

userCopy.age = 26;  // userCopy๋งŒ ๋ณ€๊ฒฝํ•˜๋ ค๊ณ  ํ–ˆ๋Š”๋ฐ...

console.log(user.age);  // 26 - ์›๋ณธ user๋„ ๋ณ€๊ฒฝ๋จ! ๐Ÿ˜ฑ

์œ„ ์ฝ”๋“œ์—์„œ ์šฐ๋ฆฌ๋Š” userCopy๋งŒ ๋ณ€๊ฒฝํ•˜๋ ค๊ณ  ํ–ˆ์ง€๋งŒ, ์‹ค์ œ๋กœ๋Š” ์›๋ณธ user ๊ฐ์ฒด๋„ ํ•จ๊ป˜ ๋ณ€๊ฒฝ๋˜์—ˆ์–ด. ์ด๊ฒŒ ๋ฐ”๋กœ ๊ฐ€๋ณ€์„ฑ์˜ ํ•จ์ •์ด์•ผ! ์ด๋Ÿฐ ํ˜„์ƒ์„ "์˜๋„์น˜ ์•Š์€ ๋ถ€์ž‘์šฉ(side effect)"์ด๋ผ๊ณ  ํ•ด.

๐Ÿšจ ๊ฐ€๋ณ€์„ฑ์ด ์ผ์œผํ‚ค๋Š” ์ฃผ์š” ๋ฌธ์ œ๋“ค

  1. ์˜ˆ์ธก ๋ถˆ๊ฐ€๋Šฅํ•œ ์ฝ”๋“œ: ๊ฐ์ฒด๊ฐ€ ์–ธ์ œ ์–ด๋””์„œ ๋ณ€๊ฒฝ๋ ์ง€ ์•Œ ์ˆ˜ ์—†์–ด ์ฝ”๋“œ ํ๋ฆ„์„ ์ถ”์ ํ•˜๊ธฐ ์–ด๋ ค์›Œ์ ธ.
  2. ๋ฒ„๊ทธ ์ฐพ๊ธฐ ์–ด๋ ค์›€: "์ด ๋ฐ์ดํ„ฐ๋Š” ๋ถ„๋ช… ์ด๋ ‡๊ฒŒ ์„ค์ •ํ–ˆ๋Š”๋ฐ ์™œ ๋ฐ”๋€Œ์—ˆ์ง€?" ๊ฐ™์€ ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•ด.
  3. ํ•จ์ˆ˜ ์ˆœ์ˆ˜์„ฑ ํ›ผ์†: ๊ฐ™์€ ์ž…๋ ฅ์— ํ•ญ์ƒ ๊ฐ™์€ ์ถœ๋ ฅ์„ ๋ณด์žฅํ•˜๋Š” ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค๊ธฐ ์–ด๋ ค์›Œ์ ธ.
  4. ํ…Œ์ŠคํŠธ ๋ณต์žก์„ฑ ์ฆ๊ฐ€: ๊ฐ€๋ณ€ ๋ฐ์ดํ„ฐ๋Š” ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์„ ์„ค์ •ํ•˜๊ณ  ๊ฒ€์ฆํ•˜๊ธฐ ์–ด๋ ต๊ฒŒ ๋งŒ๋“ค์–ด.
  5. ๋™์‹œ์„ฑ ๋ฌธ์ œ: ์—ฌ๋Ÿฌ ํ•จ์ˆ˜๊ฐ€ ๋™์‹œ์— ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•˜๋ฉด ๊ฒฝ์Ÿ ์ƒํƒœ(race condition)๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด.
user name: '์ฒ ์ˆ˜' age: 25 userCopy name: '์ฒ ์ˆ˜' age: 26 ๊ฐ™์€ ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐ (๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ ๊ณต์œ ) ๊ฐ€๋ณ€์„ฑ ๋ฌธ์ œ: userCopy๋ฅผ ๋ณ€๊ฒฝํ•˜๋ฉด user๋„ ํ•จ๊ป˜ ๋ณ€๊ฒฝ๋จ

์ด๋Ÿฐ ๋ฌธ์ œ๋“ค ๋•Œ๋ฌธ์— ํ˜„๋Œ€ JavaScript ๊ฐœ๋ฐœ์—์„œ๋Š” ๋ถˆ๋ณ€์„ฑ์„ ์ ๊ทน์ ์œผ๋กœ ๋„์ž…ํ•˜๊ณ  ์žˆ์–ด. React, Redux, Vue ๊ฐ™์€ ์ธ๊ธฐ ํ”„๋ ˆ์ž„์›Œํฌ์™€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค๋„ ๋ถˆ๋ณ€์„ฑ ์›์น™์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์„ค๊ณ„๋˜์—ˆ์ง€. ํŠนํžˆ ์ปดํฌ๋„ŒํŠธ ๊ธฐ๋ฐ˜ ์•„ํ‚คํ…์ฒ˜์—์„œ๋Š” ์ƒํƒœ ๋ณ€ํ™”๋ฅผ ์˜ˆ์ธก ๊ฐ€๋Šฅํ•˜๊ฒŒ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ๋งค์šฐ ์ค‘์š”ํ•˜๊ฑฐ๋“ . ๐Ÿงฉ

3. ๋ถˆ๋ณ€์„ฑ ๊ตฌํ˜„ํ•˜๊ธฐ: ๊ธฐ๋ณธ ํ…Œํฌ๋‹‰ ๐Ÿ› ๏ธ

์ด์ œ JavaScript์—์„œ ๋ถˆ๋ณ€์„ฑ์„ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์•Œ์•„๋ณด์ž! ๊ธฐ๋ณธ์ ์ธ ๋ฐฉ๋ฒ•๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ ์ ์  ๊ณ ๊ธ‰ ํ…Œํฌ๋‹‰์œผ๋กœ ๋‚˜์•„๊ฐˆ๊ฒŒ. ๐Ÿ˜Ž

๊ธฐ๋ณธ ์›์‹œ ํƒ€์ž…์€ ์ด๋ฏธ ๋ถˆ๋ณ€์ด์•ผ! ๐Ÿ‘

JavaScript์˜ ์›์‹œ ํƒ€์ž…(primitive types)์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ถˆ๋ณ€์ด์•ผ:

let a = 5;
let b = a;  // ๊ฐ’ ๋ณต์‚ฌ
b = 10;     // b๋งŒ ๋ณ€๊ฒฝ๋จ

console.log(a);  // 5 (๋ณ€๊ฒฝ๋˜์ง€ ์•Š์Œ)
console.log(b);  // 10

ํ•˜์ง€๋งŒ ๊ฐ์ฒด์™€ ๋ฐฐ์—ด์€ ์ฐธ์กฐ ํƒ€์ž…์ด๋ผ ํŠน๋ณ„ํ•œ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•ด. ์•„๋ž˜ ๋ฐฉ๋ฒ•๋“ค์„ ํ†ตํ•ด ๋ถˆ๋ณ€์„ฑ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์–ด:

1. ์–•์€ ๋ณต์‚ฌ(Shallow Copy) ๋ฐฉ๋ฒ•๋“ค ๐Ÿ“‹

๊ฐ์ฒด ๋ณต์‚ฌํ•˜๊ธฐ

// 1. ์Šคํ”„๋ ˆ๋“œ ์—ฐ์‚ฐ์ž ์‚ฌ์šฉ (ES6+)
const original = { name: '์˜ํฌ', age: 28 };
const copy = { ...original, age: 29 };  // ์ƒˆ ๊ฐ์ฒด ์ƒ์„ฑ + age ์†์„ฑ ๋ณ€๊ฒฝ

// 2. Object.assign() ์‚ฌ์šฉ
const anotherCopy = Object.assign({}, original, { age: 30 });

console.log(original);    // { name: '์˜ํฌ', age: 28 }
console.log(copy);        // { name: '์˜ํฌ', age: 29 }
console.log(anotherCopy); // { name: '์˜ํฌ', age: 30 }

๋ฐฐ์—ด ๋ณต์‚ฌํ•˜๊ธฐ

// 1. ์Šคํ”„๋ ˆ๋“œ ์—ฐ์‚ฐ์ž ์‚ฌ์šฉ
const numbers = [1, 2, 3];
const newNumbers = [...numbers, 4];  // [1, 2, 3, 4]

// 2. concat() ์‚ฌ์šฉ
const moreNumbers = numbers.concat(5);  // [1, 2, 3, 5]

// 3. slice() ์‚ฌ์šฉ
const numbersCopy = numbers.slice();  // [1, 2, 3]

โš ๏ธ ์ฃผ์˜: ์–•์€ ๋ณต์‚ฌ๋Š” ์ค‘์ฒฉ๋œ ๊ฐ์ฒด๋‚˜ ๋ฐฐ์—ด์—์„œ๋Š” ๋ถˆ๋ณ€์„ฑ์„ ๋ณด์žฅํ•˜์ง€ ์•Š์•„! 1๋‹จ๊ณ„ ๊นŠ์ด์—์„œ๋งŒ ์ƒˆ ์ฐธ์กฐ๊ฐ€ ์ƒ์„ฑ๋˜๊ณ , ๋‚ด๋ถ€ ๊ฐ์ฒด๋Š” ์—ฌ์ „ํžˆ ์›๋ณธ๊ณผ ๊ฐ™์€ ์ฐธ์กฐ๋ฅผ ๊ณต์œ ํ•˜๊ฒŒ ๋ผ.

2. ๊นŠ์€ ๋ณต์‚ฌ(Deep Copy) ๊ตฌํ˜„ํ•˜๊ธฐ ๐Ÿ”

// 1. JSON์„ ํ™œ์šฉํ•œ ๊นŠ์€ ๋ณต์‚ฌ (๊ฐ„๋‹จํ•˜์ง€๋งŒ ์ œํ•œ์ )
const original = { user: { name: '๋ฏผ์ˆ˜', hobbies: ['์ถ•๊ตฌ', '๊ฒŒ์ž„'] } };
const deepCopy = JSON.parse(JSON.stringify(original));

// 2. ๊ตฌ์กฐ๋ถ„ํ•ด์™€ ์Šคํ”„๋ ˆ๋“œ๋ฅผ ํ™œ์šฉํ•œ ์ˆ˜๋™ ๊นŠ์€ ๋ณต์‚ฌ
const manualDeepCopy = {
  ...original,
  user: {
    ...original.user,
    hobbies: [...original.user.hobbies]
  }
};

JSON ๋ฐฉ์‹์€ ํ•จ์ˆ˜, undefined, Symbol ๋“ฑ์„ ์ฒ˜๋ฆฌํ•˜์ง€ ๋ชปํ•˜๋Š” ํ•œ๊ณ„๊ฐ€ ์žˆ์–ด. ์‹ค๋ฌด์—์„œ๋Š” ๋ณดํ†ต ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, ํ•„์š”์— ๋งž๊ฒŒ ์ง์ ‘ ๊นŠ์€ ๋ณต์‚ฌ ํ•จ์ˆ˜๋ฅผ ๊ตฌํ˜„ํ•ด.

3. ๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ ๋‹ค๋ฃจ๊ธฐ ์œ„ํ•œ ํ•จ์ˆ˜ํ˜• ์ ‘๊ทผ๋ฒ• ๐Ÿง 

ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฐฉ์‹์„ ํ™œ์šฉํ•˜๋ฉด ๋ถˆ๋ณ€์„ฑ์„ ๋” ์šฐ์•„ํ•˜๊ฒŒ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์–ด:

// ๋ฐฐ์—ด์˜ ๋ถˆ๋ณ€์  ์กฐ์ž‘
const numbers = [1, 2, 3, 4, 5];

// โŒ ๊ฐ€๋ณ€์  ๋ฐฉ์‹ (์›๋ณธ ๋ณ€๊ฒฝ)
// numbers.push(6);
// numbers.pop();
// numbers[0] = 10;

// โœ… ๋ถˆ๋ณ€์  ๋ฐฉ์‹ (์ƒˆ ๋ฐฐ์—ด ๋ฐ˜ํ™˜)
const added = [...numbers, 6];        // ์ถ”๊ฐ€
const removed = numbers.slice(0, -1);  // ๋งˆ์ง€๋ง‰ ์š”์†Œ ์ œ๊ฑฐ
const replaced = [10, ...numbers.slice(1)];  // ์ฒซ ์š”์†Œ ๊ต์ฒด
const filtered = numbers.filter(n => n % 2 === 0);  // ์ง์ˆ˜๋งŒ ํ•„ํ„ฐ๋ง
const mapped = numbers.map(n => n * 2);  // ๋ชจ๋“  ์š”์†Œ 2๋ฐฐ๋กœ

๊ฐ์ฒด๋„ ๋น„์Šทํ•œ ํŒจํ„ด์œผ๋กœ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์–ด:

const user = {
  name: '์ง€๋ฏผ',
  age: 27,
  address: {
    city: '์„œ์šธ',
    district: '๊ฐ•๋‚จ๊ตฌ'
  }
};

// ๋ถˆ๋ณ€์ ์œผ๋กœ ์ค‘์ฒฉ ์†์„ฑ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ
const updatedUser = {
  ...user,
  age: 28,  // ๋‚˜์ด ์—…๋ฐ์ดํŠธ
  address: {
    ...user.address,
    district: '์†กํŒŒ๊ตฌ'  // ๊ตฌ๋งŒ ๋ณ€๊ฒฝ
  }
};
๋ถˆ๋ณ€์„ฑ ๊ตฌํ˜„ ๋ฐฉ์‹ ๋น„๊ต ๊ฐ€๋ณ€์  ๋ฐฉ์‹ array.push(item) obj.property = newValue ํŠน์ง• โœ“ ์ฝ”๋“œ๊ฐ€ ๊ฐ„๊ฒฐํ•จ โœ— ์›๋ณธ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ โœ— ๋ถ€์ž‘์šฉ ๋ฐœ์ƒ ๊ฐ€๋Šฅ์„ฑ ๋ถˆ๋ณ€์  ๋ฐฉ์‹ [...array, item] { ...obj, property: newValue } ํŠน์ง• โœ“ ์›๋ณธ ๋ฐ์ดํ„ฐ ๋ณด์กด โœ“ ์˜ˆ์ธก ๊ฐ€๋Šฅํ•œ ์ฝ”๋“œ โœ“ ๋ณ€๊ฒฝ ์ถ”์  ์šฉ์ด ๋ถˆ๋ณ€์„ฑ์€ ์ฝ”๋“œ์˜ ์•ˆ์ •์„ฑ๊ณผ ์˜ˆ์ธก ๊ฐ€๋Šฅ์„ฑ์„ ๋†’์—ฌ์ค๋‹ˆ๋‹ค

์ด๋Ÿฐ ํŒจํ„ด๋“ค์„ ์ตํžˆ๋ฉด ๋ณต์žกํ•œ ์ค‘์ฒฉ ๊ตฌ์กฐ์—์„œ๋„ ๋ถˆ๋ณ€์„ฑ์„ ์œ ์ง€ํ•˜๋ฉด์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์–ด. ํ•˜์ง€๋งŒ ๊ตฌ์กฐ๊ฐ€ ๊นŠ์–ด์งˆ์ˆ˜๋ก ์ฝ”๋“œ๊ฐ€ ๋ณต์žกํ•ด์ง€๋Š” ๋‹จ์ ์ด ์žˆ์ง€. ์ด๋Ÿด ๋•Œ ์˜์† ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋‚˜ ์ „๋ฌธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๋„์›€์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์–ด. ๋‹ค์Œ ์„น์…˜์—์„œ ์‚ดํŽด๋ณด์ž! ๐Ÿš€

4. ์˜์† ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ž€? ๐ŸŒณ

์˜์† ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ(Persistent Data Structures)๋Š” ๋ถˆ๋ณ€์„ฑ์„ ํšจ์œจ์ ์œผ๋กœ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•œ ํŠน๋ณ„ํ•œ ์ž๋ฃŒ๊ตฌ์กฐ์•ผ. "์˜์†์ "์ด๋ผ๋Š” ๋ง์€ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์˜ ์ด์ „ ๋ฒ„์ „์ด ํ•ญ์ƒ ๋ณด์กด๋œ๋‹ค๋Š” ์˜๋ฏธ์•ผ. ๐Ÿ•ฐ๏ธ

์˜์† ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์˜ ํ•ต์‹ฌ ๊ฐœ๋…

์ผ๋ฐ˜์ ์ธ ๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ ๊ตฌํ˜„์€ ๋ณ€๊ฒฝ์ด ํ•„์š”ํ•  ๋•Œ๋งˆ๋‹ค ์ „์ฒด ๋ฐ์ดํ„ฐ๋ฅผ ๋ณต์‚ฌํ•ด์•ผ ํ•ด์„œ ๋ฉ”๋ชจ๋ฆฌ์™€ ์„ฑ๋Šฅ ์ธก๋ฉด์—์„œ ๋น„ํšจ์œจ์ ์ผ ์ˆ˜ ์žˆ์–ด. ํ•˜์ง€๋งŒ ์˜์† ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋Š” ๊ตฌ์กฐ ๊ณต์œ (Structural Sharing)๋ผ๋Š” ๊ธฐ๋ฒ•์„ ์‚ฌ์šฉํ•ด ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด.

๊ตฌ์กฐ ๊ณต์œ ๋ž€ ์ƒˆ ๋ฒ„์ „์„ ๋งŒ๋“ค ๋•Œ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์€ ๋ถ€๋ถ„์€ ์›๋ณธ๊ณผ ๊ณต์œ ํ•˜๊ณ , ๋ณ€๊ฒฝ๋œ ๋ถ€๋ถ„๋งŒ ์ƒˆ๋กœ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ์‹์ด์•ผ. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์„ ํฌ๊ฒŒ ์ค„์ด๋ฉด์„œ๋„ ๋ถˆ๋ณ€์„ฑ์˜ ์ด์ ์„ ๋ˆ„๋ฆด ์ˆ˜ ์žˆ์ง€!

ํŠธ๋ผ์ด(Trie)์™€ ๊ฐ™์€ ์˜์† ์ž๋ฃŒ๊ตฌ์กฐ ์ดํ•ดํ•˜๊ธฐ ๐ŸŒฒ

์˜์† ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์˜ ๋Œ€ํ‘œ์ ์ธ ์˜ˆ๋กœ๋Š” ํŠธ๋ผ์ด(Trie), ํŠนํžˆ ๊ทธ ๋ณ€ํ˜•์ธ HAMT(Hash Array Mapped Trie)๊ฐ€ ์žˆ์–ด. ์ด ๊ตฌ์กฐ๋“ค์€ Immutable.js๋‚˜ Immer ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๊ธฐ๋ฐ˜์ด ๋˜๋Š” ๊ฐœ๋…์ด์•ผ.

๊ตฌ์กฐ ๊ณต์œ (Structural Sharing) ๊ฐœ๋… A B C D E F G ์›๋ณธ ํŠธ๋ฆฌ A' B' C D' E F G ๋ณ€๊ฒฝ๋œ ํŠธ๋ฆฌ ๋ณ€๊ฒฝ๋œ ๋…ธ๋“œ(์ดˆ๋ก์ƒ‰)๋งŒ ์ƒˆ๋กœ ์ƒ์„ฑํ•˜๊ณ  ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์€ ๋ถ€๋ถ„(ํŒŒ๋ž€์ƒ‰)์€ ์›๋ณธ๊ณผ ๊ณต์œ 

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

์˜์† ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์˜ ์žฅ์  ๐ŸŒŸ

  1. ํšจ์œจ์ ์ธ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ: ๋ณ€๊ฒฝ๋œ ๋ถ€๋ถ„๋งŒ ์ƒˆ๋กœ ์ƒ์„ฑํ•˜๋ฏ€๋กœ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์ด ์ตœ์†Œํ™”๋ผ.
  2. ๋น ๋ฅธ ๋น„๊ต ์—ฐ์‚ฐ: ์ฐธ์กฐ ๋™๋“ฑ์„ฑ ๊ฒ€์‚ฌ๋งŒ์œผ๋กœ ๋ณ€๊ฒฝ ์—ฌ๋ถ€๋ฅผ ๋น ๋ฅด๊ฒŒ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด.
  3. ์‹œ๊ฐ„ ์—ฌํ–‰(Time Travel): ๋ชจ๋“  ์ด์ „ ๋ฒ„์ „์ด ๋ณด์กด๋˜๋ฏ€๋กœ ์ƒํƒœ ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ์‰ฝ๊ฒŒ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด.
  4. ํŠธ๋žœ์žญ์…˜ ์•ˆ์ „์„ฑ: ์ž‘์—… ์ค‘๊ฐ„์— ์‹คํŒจํ•ด๋„ ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ์ด ์œ ์ง€๋ผ.
  5. ๋™์‹œ์„ฑ ์ง€์›: ์—ฌ๋Ÿฌ ์ž‘์—…์ด ๋™์‹œ์— ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์–ด.

์‹ค์ œ ์˜ˆ: Immutable.js์˜ List ์‚ฌ์šฉ ์˜ˆ์ œ

import { List } from 'immutable';

// ๋ถˆ๋ณ€ ๋ฆฌ์ŠคํŠธ ์ƒ์„ฑ
const list1 = List([1, 2, 3, 4]);

// ์ƒˆ ๋ฆฌ์ŠคํŠธ ์ƒ์„ฑ (์›๋ณธ์€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์Œ)
const list2 = list1.push(5);
const list3 = list1.set(0, 10);

console.log(list1.toArray());  // [1, 2, 3, 4] - ์›๋ณธ ์œ ์ง€
console.log(list2.toArray());  // [1, 2, 3, 4, 5]
console.log(list3.toArray());  // [10, 2, 3, 4]

// ํšจ์œจ์ ์ธ ๋น„๊ต
console.log(list1 === list2);  // false - ๋‹ค๋ฅธ ๊ฐ์ฒด
console.log(list1.equals(list2));  // false - ๋‚ด์šฉ์ด ๋‹ค๋ฆ„

์˜์† ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋Š” ์žฌ๋Šฅ๋„ท์—์„œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ด€๋ จ ์žฌ๋Šฅ์„ ๊ณต์œ ํ•˜๋Š” ๊ฐœ๋ฐœ์ž๋“ค ์‚ฌ์ด์—์„œ๋„ ์ธ๊ธฐ ์žˆ๋Š” ์ฃผ์ œ์•ผ. ํŠนํžˆ ๋Œ€๊ทœ๋ชจ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ฐœ๋ฐœํ•  ๋•Œ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๊ฑฐ๋“ . ๋‹ค์Œ ์„น์…˜์—์„œ๋Š” ์ด๋Ÿฐ ๊ฐœ๋…์„ ์‹ค์ œ๋กœ ๊ตฌํ˜„ํ•œ ์ธ๊ธฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์„ ์‚ดํŽด๋ณผ๊ฒŒ! ๐Ÿ“š

6. ์‹ค์ „ ์˜ˆ์ œ: ๋ถˆ๋ณ€์„ฑ์œผ๋กœ ์„ฑ๋Šฅ ์ตœ์ ํ™”ํ•˜๊ธฐ โšก

์ด๋ก ์€ ์ถฉ๋ถ„ํžˆ ์•Œ์•„๋ดค์œผ๋‹ˆ, ์ด์ œ ์‹ค์ œ ์ฝ”๋“œ์—์„œ ๋ถˆ๋ณ€์„ฑ์„ ํ™œ์šฉํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ดํŽด๋ณด์ž! ๐Ÿ”

React์—์„œ ๋ถˆ๋ณ€์„ฑ์„ ํ™œ์šฉํ•œ ๋ Œ๋”๋ง ์ตœ์ ํ™” ๐Ÿ”„

React๋Š” ์ปดํฌ๋„ŒํŠธ์˜ ๋ฆฌ๋ Œ๋”๋ง ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•  ๋•Œ ์–•์€ ๋น„๊ต(shallow comparison)๋ฅผ ์‚ฌ์šฉํ•ด. ๋ถˆ๋ณ€์„ฑ์„ ํ™œ์šฉํ•˜๋ฉด ์ด ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ํ™œ์šฉํ•ด ๋ถˆํ•„์š”ํ•œ ๋ Œ๋”๋ง์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์–ด.

React.memo์™€ ๋ถˆ๋ณ€์„ฑ์„ ํ™œ์šฉํ•œ ์ตœ์ ํ™”

import React, { useState } from 'react';
import { produce } from 'immer';

// ์ตœ์ ํ™”๋œ ํ• ์ผ ํ•ญ๋ชฉ ์ปดํฌ๋„ŒํŠธ
const TodoItem = React.memo(({ todo, onToggle }) => {
  console.log(`TodoItem ๋ Œ๋”๋ง: ${todo.text}`);
  
  return (
    <div style="{{" textdecoration: todo.completed :>
      <input type="checkbox" checked onchange="{()"> onToggle(todo.id)}
      />
      {todo.text}
    </div>
  );
});

// ํ• ์ผ ๋ชฉ๋ก ์ปดํฌ๋„ŒํŠธ
function TodoList() {
  const [todos, setTodos] = useState([
    { id: 1, text: '๋ถˆ๋ณ€์„ฑ ๊ณต๋ถ€ํ•˜๊ธฐ', completed: false },
    { id: 2, text: 'React ์ตœ์ ํ™”ํ•˜๊ธฐ', completed: false },
    { id: 3, text: '์˜์† ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ ์ดํ•ดํ•˜๊ธฐ', completed: false }
  ]);
  
  // ๋ถˆ๋ณ€์ ์œผ๋กœ ํ• ์ผ ์ƒํƒœ ํ† ๊ธ€ํ•˜๊ธฐ
  const handleToggle = (id) => {
    setTodos(
      produce(todos, draft => {
        const todo = draft.find(t => t.id === id);
        if (todo) {
          todo.completed = !todo.completed;
        }
      })
    );
  };
  
  return (
    <div>
      <h2>ํ•  ์ผ ๋ชฉ๋ก</h2>
      {todos.map(todo => (
        <todoitem key="{todo.id}" todo="{todo}" ontoggle="{handleToggle}"></todoitem>
      ))}
    </div>
  );
}

์œ„ ์˜ˆ์ œ์—์„œ React.memo์™€ Immer๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด ์ตœ์ ํ™”๋ฅผ ๊ตฌํ˜„ํ–ˆ์–ด. ํŠน์ • ํ• ์ผ ํ•ญ๋ชฉ์„ ํ† ๊ธ€ํ•  ๋•Œ, ํ•ด๋‹น ํ•ญ๋ชฉ๋งŒ ์ƒˆ ๊ฐ์ฒด๋กœ ์ƒ์„ฑ๋˜๊ณ  ๋‚˜๋จธ์ง€๋Š” ๊ทธ๋Œ€๋กœ ์œ ์ง€๋ผ. ๋”ฐ๋ผ์„œ ๋ณ€๊ฒฝ๋œ ํ•ญ๋ชฉ๋งŒ ๋ฆฌ๋ Œ๋”๋ง๋˜์–ด ์„ฑ๋Šฅ์ด ํ–ฅ์ƒ๋ผ!

์ตœ์ ํ™” ์ „/ํ›„ ๋น„๊ต

๋ถˆ๋ณ€์„ฑ ๊ธฐ๋ฐ˜ ์ตœ์ ํ™” ํšจ๊ณผ ์ตœ์ ํ™” ์ „ (๊ฐ€๋ณ€ ๋ฐ์ดํ„ฐ) Todo 1 ๋ Œ๋”๋ง Todo 2 ๋ Œ๋”๋ง Todo 3 ๋ Œ๋”๋ง ํ•˜๋‚˜์˜ ํ•ญ๋ชฉ์ด ๋ณ€๊ฒฝ๋˜์–ด๋„ ๋ชจ๋“  ํ•ญ๋ชฉ์ด ๋‹ค์‹œ ๋ Œ๋”๋ง๋จ ์ตœ์ ํ™” ํ›„ (๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ) Todo 1 ๋ Œ๋”๋ง ์•ˆ๋จ Todo 2 ๋ Œ๋”๋ง (๋ณ€๊ฒฝ๋จ) Todo 3 ๋ Œ๋”๋ง ์•ˆ๋จ ๋ณ€๊ฒฝ๋œ ํ•ญ๋ชฉ๋งŒ ๋‹ค์‹œ ๋ Œ๋”๋ง๋จ

Redux์—์„œ ๋ถˆ๋ณ€์„ฑ์„ ํ™œ์šฉํ•œ ์ƒํƒœ ๊ด€๋ฆฌ ์ตœ์ ํ™” ๐Ÿ“Š

Redux๋Š” ๋ถˆ๋ณ€์„ฑ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๋Š” ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์•ผ. Immer๋ฅผ Redux์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด ๋ณต์žกํ•œ ์ƒํƒœ ์—…๋ฐ์ดํŠธ๋ฅผ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์–ด.

Redux + Immer๋ฅผ ํ™œ์šฉํ•œ ์ƒํƒœ ๊ด€๋ฆฌ

// Redux ๋ฆฌ๋“€์„œ์— Immer ์ ์šฉํ•˜๊ธฐ
import { createSlice } from '@reduxjs/toolkit';

const todosSlice = createSlice({
  name: 'todos',
  initialState: {
    items: [],
    loading: false,
    error: null
  },
  reducers: {
    // createSlice๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ Immer๋ฅผ ์‚ฌ์šฉํ•จ
    addTodo: (state, action) => {
      // ๊ฐ€๋ณ€์ ์ธ ์ฝ”๋“œ ์Šคํƒ€์ผ๋กœ ๋ถˆ๋ณ€ ์—…๋ฐ์ดํŠธ ์ž‘์„ฑ
      state.items.push({
        id: Date.now(),
        text: action.payload,
        completed: false
      });
    },
    toggleTodo: (state, action) => {
      const todo = state.items.find(item => item.id === action.payload);
      if (todo) {
        todo.completed = !todo.completed;
      }
    },
    removeTodo: (state, action) => {
      state.items = state.items.filter(item => item.id !== action.payload);
    }
  }
});

export const { addTodo, toggleTodo, removeTodo } = todosSlice.actions;
export default todosSlice.reducer;

Redux Toolkit์€ ๋‚ด๋ถ€์ ์œผ๋กœ Immer๋ฅผ ์‚ฌ์šฉํ•ด ์ง๊ด€์ ์ธ ์ฝ”๋“œ ์Šคํƒ€์ผ๋กœ ๋ถˆ๋ณ€ ์—…๋ฐ์ดํŠธ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค˜. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ฝ”๋“œ๋Š” ๊ฐ„๊ฒฐํ•ด์ง€๋ฉด์„œ๋„ ๋ถˆ๋ณ€์„ฑ์˜ ๋ชจ๋“  ์ด์ ์„ ๋ˆ„๋ฆด ์ˆ˜ ์žˆ์–ด!

๋Œ€๊ทœ๋ชจ ๋ฐ์ดํ„ฐ์…‹์—์„œ์˜ ์„ฑ๋Šฅ ์ตœ์ ํ™” ๐Ÿ“ˆ

์ˆ˜์ฒœ ๊ฐœ์˜ ํ•ญ๋ชฉ์„ ๊ฐ€์ง„ ๋Œ€๊ทœ๋ชจ ๋ฐ์ดํ„ฐ์…‹์„ ๋‹ค๋ฃฐ ๋•Œ๋Š” ์˜์† ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ํ™œ์šฉํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ํŠนํžˆ ์œ ์šฉํ•ด.

Immutable.js๋ฅผ ํ™œ์šฉํ•œ ๋Œ€๊ทœ๋ชจ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ

import { List, Map } from 'immutable';

// ๋Œ€๊ทœ๋ชจ ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ (์ˆ˜์ฒœ ๋ช…)
let users = List();

// ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ
for (let i = 0; i < 10000; i++) {
  users = users.push(Map({
    id: i,
    name: `User ${i}`,
    active: Math.random() > 0.5
  }));
}

// ํŠน์ • ์กฐ๊ฑด์˜ ์‚ฌ์šฉ์ž๋งŒ ํ•„ํ„ฐ๋ง (๋ถˆ๋ณ€์  ์—ฐ์‚ฐ)
const activeUsers = users.filter(user => user.get('active'));

// ๋ชจ๋“  ์‚ฌ์šฉ์ž์˜ ์ด๋ฆ„์„ ๋ณ€ํ™˜ (๋ถˆ๋ณ€์  ์—ฐ์‚ฐ)
const userNames = users.map(user => user.get('name'));

// ํŠน์ • ID์˜ ์‚ฌ์šฉ์ž ์ฐพ๊ธฐ (ํšจ์œจ์ ์ธ ๊ฒ€์ƒ‰)
const findUser = (id) => {
  // ์ด์ง„ ๊ฒ€์ƒ‰ ๋“ฑ์˜ ์ตœ์ ํ™”๋œ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ํ™œ์šฉ ๊ฐ€๋Šฅ
  return users.find(user => user.get('id') === id);
};

// ํŠน์ • ์‚ฌ์šฉ์ž ์ •๋ณด ์—…๋ฐ์ดํŠธ (๋ถˆ๋ณ€์  ์—…๋ฐ์ดํŠธ)
const updateUser = (id, updates) => {
  const index = users.findIndex(user => user.get('id') === id);
  if (index !== -1) {
    users = users.update(index, user => user.merge(updates));
  }
  return users;
};

Immutable.js๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ ํšจ์œจ์ ์ธ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์™€ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•ด ๋Œ€๊ทœ๋ชจ ๋ฐ์ดํ„ฐ์…‹์—์„œ๋„ ๋น ๋ฅธ ์„ฑ๋Šฅ์„ ์ œ๊ณตํ•ด. ํŠนํžˆ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์ด ๋นˆ๋ฒˆํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰๊ณผ ์„ฑ๋Šฅ์„ ํฌ๊ฒŒ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์–ด.

โšก ์„ฑ๋Šฅ ์ตœ์ ํ™” ํŒ

  1. ์–•์€ ๋น„๊ต ํ™œ์šฉํ•˜๊ธฐ: React.memo, PureComponent, shouldComponentUpdate ๋“ฑ์„ ํ™œ์šฉํ•ด ๋ถˆ๋ณ€์„ฑ์˜ ์ด์ ์„ ์ตœ๋Œ€ํ™”ํ•˜์„ธ์š”.
  2. ์„ ํƒ์  ๋ Œ๋”๋ง: ์ƒํƒœ ๋ณ€๊ฒฝ ์‹œ ์˜ํ–ฅ๋ฐ›๋Š” ์ปดํฌ๋„ŒํŠธ๋งŒ ๋ Œ๋”๋ง๋˜๋„๋ก ์ƒํƒœ ๊ตฌ์กฐ๋ฅผ ์„ค๊ณ„ํ•˜์„ธ์š”.
  3. ์ •๊ทœํ™”๋œ ์ƒํƒœ ๊ตฌ์กฐ: ์ค‘์ฒฉ์„ ์ตœ์†Œํ™”ํ•˜๊ณ  ID ๊ธฐ๋ฐ˜ ์ฐธ์กฐ๋ฅผ ํ™œ์šฉํ•ด ์ƒํƒœ ์—…๋ฐ์ดํŠธ๋ฅผ ๋‹จ์ˆœํ™”ํ•˜์„ธ์š”.
  4. ๋ฉ”๋ชจ์ด์ œ์ด์…˜: useMemo์™€ useCallback์„ ํ™œ์šฉํ•ด ๊ณ„์‚ฐ ๋น„์šฉ์ด ํฐ ์—ฐ์‚ฐ์„ ์ตœ์ ํ™”ํ•˜์„ธ์š”.
  5. ๋ฐฐ์น˜ ์—…๋ฐ์ดํŠธ: ์—ฌ๋Ÿฌ ์ƒํƒœ ์—…๋ฐ์ดํŠธ๋ฅผ ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌํ•ด ๋ถˆํ•„์š”ํ•œ ๋ Œ๋”๋ง์„ ์ค„์ด์„ธ์š”.

์ด๋Ÿฐ ์ตœ์ ํ™” ๊ธฐ๋ฒ•๋“ค์€ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ ๋ˆˆ์— ๋„๋Š” ์„ฑ๋Šฅ ํ–ฅ์ƒ์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์–ด. ํŠนํžˆ ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค์˜ ๋ฐ˜์‘์„ฑ๊ณผ ๋ถ€๋“œ๋Ÿฌ์›€์ด ์ค‘์š”ํ•œ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋”์šฑ ๊ทธ๋ ‡์ง€. ๋‹ค์Œ ์„น์…˜์—์„œ๋Š” 2025๋…„ ํ˜„์žฌ์˜ ๋ถˆ๋ณ€์„ฑ ํŠธ๋ Œ๋“œ์™€ ๋ฏธ๋ž˜ ์ „๋ง์— ๋Œ€ํ•ด ์•Œ์•„๋ณผ๊ฒŒ! ๐Ÿ”ฎ

๋ถˆ๋ณ€์„ฑ์˜ ์„ธ๊ณ„๋กœ ๋›ฐ์–ด๋“ค ์ค€๋น„๊ฐ€ ๋˜์—ˆ๋‚˜์š”? ๐Ÿš€

์ด ๊ธ€์ด JavaScript์˜ ๋ถˆ๋ณ€์„ฑ๊ณผ ์˜์† ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์— ๋Œ€ํ•œ ์ดํ•ด๋ฅผ ๋†’์ด๋Š” ๋ฐ ๋„์›€์ด ๋˜์—ˆ๊ธธ ๋ฐ”๋ผ! ๋” ๋งŽ์€ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์ง€์‹๊ณผ ํŒ์„ ์–ป๊ณ  ์‹ถ๋‹ค๋ฉด ์žฌ๋Šฅ๋„ท์˜ '์ง€์‹์ธ์˜ ์ˆฒ'์„ ๊ณ„์† ๋ฐฉ๋ฌธํ•ด์ฃผ์„ธ์š”.

๋˜ํ•œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ด€๋ จ ๋„์›€์ด ํ•„์š”ํ•˜๊ฑฐ๋‚˜ ์ž์‹ ์˜ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์žฌ๋Šฅ์„ ๊ณต์œ ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, ์žฌ๋Šฅ๋„ท์—์„œ ๋‹ค์–‘ํ•œ ๊ฐœ๋ฐœ์ž๋“ค๊ณผ ์—ฐ๊ฒฐ๋  ์ˆ˜ ์žˆ์–ด์š”! ํ•จ๊ป˜ ์„ฑ์žฅํ•˜๋Š” ๊ฐœ๋ฐœ์ž ์ปค๋ฎค๋‹ˆํ‹ฐ์—์„œ ๋งŒ๋‚˜์š”! ๐Ÿ‘‹

1. ๋ถˆ๋ณ€์„ฑ์ด๋ž€ ๋ฌด์—‡์ธ๊ฐ€? ๊ทธ๋ฆฌ๊ณ  ์™œ ์ค‘์š”ํ• ๊นŒ? ๐Ÿค”

๋ถˆ๋ณ€์„ฑ(Immutability)์€ ํ•œ๋ฒˆ ์ƒ์„ฑ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฐœ๋…์ด์•ผ. ์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด, "ํ•œ๋ฒˆ ๋งŒ๋“ค์–ด์ง„ ๊ฒƒ์€ ๋ฐ”๊พธ์ง€ ์•Š๊ณ , ํ•„์š”ํ•˜๋ฉด ์ƒˆ๋กœ ๋งŒ๋“ ๋‹ค"๋Š” ์›์น™์ด์ง€. ๋งˆ์น˜ ๋„ˆ์˜ ์ผ๊ธฐ์žฅ์ฒ˜๋Ÿผ - ํ•œ๋ฒˆ ์“ด ๋‚ด์šฉ์„ ์ง€์šฐ๊ฐœ๋กœ ์ง€์šฐ๊ณ  ๋‹ค์‹œ ์“ฐ๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ, ์ƒˆ ํŽ˜์ด์ง€์— ์ƒˆ๋กœ์šด ๋‚ด์šฉ์„ ์“ฐ๋Š” ๊ฑฐ์•ผ. ๐Ÿ“

๐Ÿ”„ ๊ฐ€๋ณ€์„ฑ vs ๋ถˆ๋ณ€์„ฑ

๊ฐ€๋ณ€์„ฑ(Mutability):

- ๋ฐ์ดํ„ฐ๊ฐ€ ์ƒ์„ฑ๋œ ํ›„์—๋„ ๋‚ด์šฉ์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Œ
- ๊ฐ™์€ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๋ฅผ ๊ณ„์† ์‚ฌ์šฉ
- ์˜ˆ: ๋ฐฐ์—ด์˜ push(), pop(), ๊ฐ์ฒด ์†์„ฑ ์ง์ ‘ ์ˆ˜์ •

๋ถˆ๋ณ€์„ฑ(Immutability):

- ๋ฐ์ดํ„ฐ๊ฐ€ ์ƒ์„ฑ๋œ ํ›„์—๋Š” ๋‚ด์šฉ์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์Œ
- ๋ณ€๊ฒฝ์ด ํ•„์š”ํ•˜๋ฉด ์ƒˆ๋กœ์šด ๋ฉ”๋ชจ๋ฆฌ์— ๋ณต์‚ฌ๋ณธ ์ƒ์„ฑ
- ์˜ˆ: ๋ฌธ์ž์—ด ๋ฉ”์„œ๋“œ๋“ค, Array.map(), Array.filter()

์™œ ๋ถˆ๋ณ€์„ฑ์ด ์ค‘์š”ํ• ๊นŒ? ๐ŸŒŸ

  1. ์˜ˆ์ธก ๊ฐ€๋Šฅ์„ฑ: ๋ฐ์ดํ„ฐ๊ฐ€ ์–ธ์ œ ์–ด๋””์„œ ๋ณ€ํ• ์ง€ ๊ฑฑ์ •ํ•  ํ•„์š”๊ฐ€ ์—†์–ด. ํ•œ๋ฒˆ ๋งŒ๋“ค์–ด์ง„ ๋ฐ์ดํ„ฐ๋Š” ํ•ญ์ƒ ๊ฐ™์€ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๋‹ˆ๊นŒ!
  2. ๋””๋ฒ„๊น… ์šฉ์ด์„ฑ: ๋ฒ„๊ทธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ๋ฐ์ดํ„ฐ ๋ณ€ํ™”๋ฅผ ์ถ”์ ํ•˜๊ธฐ ์‰ฌ์›Œ์ ธ. "๋ˆ„๊ฐ€ ์ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”๊ฟจ์ง€?"๋ผ๋Š” ์งˆ๋ฌธ์ด ์‚ฌ๋ผ์ง€๋‹ˆ๊นŒ.
  3. ๋™์‹œ์„ฑ ์ฒ˜๋ฆฌ: ์—ฌ๋Ÿฌ ํ•จ์ˆ˜๋‚˜ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ๊ฐ™์€ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•ด๋„ ์ถฉ๋Œ์ด ์—†์–ด.
  4. ์‹œ๊ฐ„ ์—ฌํ–‰ ๋””๋ฒ„๊น…: ์ด์ „ ์ƒํƒœ๋กœ ์‰ฝ๊ฒŒ ๋Œ์•„๊ฐˆ ์ˆ˜ ์žˆ์–ด. Redux DevTools ๊ฐ™์€ ๋„๊ตฌ๊ฐ€ ์ด๋ฅผ ํ™œ์šฉํ•˜์ง€.
  5. ์ฐธ์กฐ ๋™๋“ฑ์„ฑ ์ตœ์ ํ™”: ๊ฐ์ฒด๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ๋Š”์ง€ ๋น ๋ฅด๊ฒŒ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด (๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๋งŒ ๋น„๊ตํ•˜๋ฉด ๋จ).

"๋ถˆ๋ณ€์„ฑ์€ ๋‹จ์ˆœํ•œ ์ฝ”๋”ฉ ์Šคํƒ€์ผ์ด ์•„๋‹ˆ๋ผ, ์†Œํ”„ํŠธ์›จ์–ด ์„ค๊ณ„์˜ ์ฒ ํ•™์ด๋‹ค."

- ์–ด๋–ค ํ˜„๋ช…ํ•œ ๊ฐœ๋ฐœ์ž ๐Ÿ˜‰

2. JavaScript์—์„œ์˜ ๊ฐ€๋ณ€์„ฑ ๋ฌธ์ œ์  ๐Ÿ˜ฑ

JavaScript๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ฐ€๋ณ€(mutable) ์–ธ์–ด์•ผ. ํŠนํžˆ ๊ฐ์ฒด์™€ ๋ฐฐ์—ด์€ ์ฐธ์กฐ ํƒ€์ž…์ด๋ผ์„œ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋ถ€์ž‘์šฉ(side effects)์„ ์ผ์œผํ‚ค๊ธฐ ์‰ฝ์ง€. ์•„๋ž˜ ์˜ˆ์ œ๋ฅผ ํ•œ๋ฒˆ ๋ณผ๊นŒ?

// ๊ฐ€๋ณ€์„ฑ์œผ๋กœ ์ธํ•œ ๋ฌธ์ œ ์˜ˆ์‹œ
const user = { name: '์ฒ ์ˆ˜', age: 25 };
const userCopy = user;  // ์ฐธ์กฐ๋งŒ ๋ณต์‚ฌ๋จ

userCopy.age = 26;  // userCopy๋งŒ ๋ณ€๊ฒฝํ•˜๋ ค๊ณ  ํ–ˆ๋Š”๋ฐ...

console.log(user.age);  // 26 - ์›๋ณธ user๋„ ๋ณ€๊ฒฝ๋จ! ๐Ÿ˜ฑ

์œ„ ์ฝ”๋“œ์—์„œ ์šฐ๋ฆฌ๋Š” userCopy๋งŒ ๋ณ€๊ฒฝํ•˜๋ ค๊ณ  ํ–ˆ์ง€๋งŒ, ์‹ค์ œ๋กœ๋Š” ์›๋ณธ user ๊ฐ์ฒด๋„ ํ•จ๊ป˜ ๋ณ€๊ฒฝ๋˜์—ˆ์–ด. ์ด๊ฒŒ ๋ฐ”๋กœ ๊ฐ€๋ณ€์„ฑ์˜ ํ•จ์ •์ด์•ผ! ์ด๋Ÿฐ ํ˜„์ƒ์„ "์˜๋„์น˜ ์•Š์€ ๋ถ€์ž‘์šฉ(side effect)"์ด๋ผ๊ณ  ํ•ด.

๐Ÿšจ ๊ฐ€๋ณ€์„ฑ์ด ์ผ์œผํ‚ค๋Š” ์ฃผ์š” ๋ฌธ์ œ๋“ค

  1. ์˜ˆ์ธก ๋ถˆ๊ฐ€๋Šฅํ•œ ์ฝ”๋“œ: ๊ฐ์ฒด๊ฐ€ ์–ธ์ œ ์–ด๋””์„œ ๋ณ€๊ฒฝ๋ ์ง€ ์•Œ ์ˆ˜ ์—†์–ด ์ฝ”๋“œ ํ๋ฆ„์„ ์ถ”์ ํ•˜๊ธฐ ์–ด๋ ค์›Œ์ ธ.
  2. ๋ฒ„๊ทธ ์ฐพ๊ธฐ ์–ด๋ ค์›€: "์ด ๋ฐ์ดํ„ฐ๋Š” ๋ถ„๋ช… ์ด๋ ‡๊ฒŒ ์„ค์ •ํ–ˆ๋Š”๋ฐ ์™œ ๋ฐ”๋€Œ์—ˆ์ง€?" ๊ฐ™์€ ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•ด.
  3. ํ•จ์ˆ˜ ์ˆœ์ˆ˜์„ฑ ํ›ผ์†: ๊ฐ™์€ ์ž…๋ ฅ์— ํ•ญ์ƒ ๊ฐ™์€ ์ถœ๋ ฅ์„ ๋ณด์žฅํ•˜๋Š” ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค๊ธฐ ์–ด๋ ค์›Œ์ ธ.
  4. ํ…Œ์ŠคํŠธ ๋ณต์žก์„ฑ ์ฆ๊ฐ€: ๊ฐ€๋ณ€ ๋ฐ์ดํ„ฐ๋Š” ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์„ ์„ค์ •ํ•˜๊ณ  ๊ฒ€์ฆํ•˜๊ธฐ ์–ด๋ ต๊ฒŒ ๋งŒ๋“ค์–ด.
  5. ๋™์‹œ์„ฑ ๋ฌธ์ œ: ์—ฌ๋Ÿฌ ํ•จ์ˆ˜๊ฐ€ ๋™์‹œ์— ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•˜๋ฉด ๊ฒฝ์Ÿ ์ƒํƒœ(race condition)๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด.
user name: '์ฒ ์ˆ˜' age: 25 userCopy name: '์ฒ ์ˆ˜' age: 26 ๊ฐ™์€ ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐ (๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ ๊ณต์œ ) ๊ฐ€๋ณ€์„ฑ ๋ฌธ์ œ: userCopy๋ฅผ ๋ณ€๊ฒฝํ•˜๋ฉด user๋„ ํ•จ๊ป˜ ๋ณ€๊ฒฝ๋จ

์ด๋Ÿฐ ๋ฌธ์ œ๋“ค ๋•Œ๋ฌธ์— ํ˜„๋Œ€ JavaScript ๊ฐœ๋ฐœ์—์„œ๋Š” ๋ถˆ๋ณ€์„ฑ์„ ์ ๊ทน์ ์œผ๋กœ ๋„์ž…ํ•˜๊ณ  ์žˆ์–ด. React, Redux, Vue ๊ฐ™์€ ์ธ๊ธฐ ํ”„๋ ˆ์ž„์›Œํฌ์™€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค๋„ ๋ถˆ๋ณ€์„ฑ ์›์น™์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์„ค๊ณ„๋˜์—ˆ์ง€. ํŠนํžˆ ์ปดํฌ๋„ŒํŠธ ๊ธฐ๋ฐ˜ ์•„ํ‚คํ…์ฒ˜์—์„œ๋Š” ์ƒํƒœ ๋ณ€ํ™”๋ฅผ ์˜ˆ์ธก ๊ฐ€๋Šฅํ•˜๊ฒŒ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ๋งค์šฐ ์ค‘์š”ํ•˜๊ฑฐ๋“ . ๐Ÿงฉ

3. ๋ถˆ๋ณ€์„ฑ ๊ตฌํ˜„ํ•˜๊ธฐ: ๊ธฐ๋ณธ ํ…Œํฌ๋‹‰ ๐Ÿ› ๏ธ

์ด์ œ JavaScript์—์„œ ๋ถˆ๋ณ€์„ฑ์„ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์•Œ์•„๋ณด์ž! ๊ธฐ๋ณธ์ ์ธ ๋ฐฉ๋ฒ•๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ ์ ์  ๊ณ ๊ธ‰ ํ…Œํฌ๋‹‰์œผ๋กœ ๋‚˜์•„๊ฐˆ๊ฒŒ. ๐Ÿ˜Ž

๊ธฐ๋ณธ ์›์‹œ ํƒ€์ž…์€ ์ด๋ฏธ ๋ถˆ๋ณ€์ด์•ผ! ๐Ÿ‘

JavaScript์˜ ์›์‹œ ํƒ€์ž…(primitive types)์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ถˆ๋ณ€์ด์•ผ:

let a = 5;
let b = a;  // ๊ฐ’ ๋ณต์‚ฌ
b = 10;     // b๋งŒ ๋ณ€๊ฒฝ๋จ

console.log(a);  // 5 (๋ณ€๊ฒฝ๋˜์ง€ ์•Š์Œ)
console.log(b);  // 10

ํ•˜์ง€๋งŒ ๊ฐ์ฒด์™€ ๋ฐฐ์—ด์€ ์ฐธ์กฐ ํƒ€์ž…์ด๋ผ ํŠน๋ณ„ํ•œ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•ด. ์•„๋ž˜ ๋ฐฉ๋ฒ•๋“ค์„ ํ†ตํ•ด ๋ถˆ๋ณ€์„ฑ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์–ด:

1. ์–•์€ ๋ณต์‚ฌ(Shallow Copy) ๋ฐฉ๋ฒ•๋“ค ๐Ÿ“‹

๊ฐ์ฒด ๋ณต์‚ฌํ•˜๊ธฐ

// 1. ์Šคํ”„๋ ˆ๋“œ ์—ฐ์‚ฐ์ž ์‚ฌ์šฉ (ES6+)
const original = { name: '์˜ํฌ', age: 28 };
const copy = { ...original, age: 29 };  // ์ƒˆ ๊ฐ์ฒด ์ƒ์„ฑ + age ์†์„ฑ ๋ณ€๊ฒฝ

// 2. Object.assign() ์‚ฌ์šฉ
const anotherCopy = Object.assign({}, original, { age: 30 });

console.log(original);    // { name: '์˜ํฌ', age: 28 }
console.log(copy);        // { name: '์˜ํฌ', age: 29 }
console.log(anotherCopy); // { name: '์˜ํฌ', age: 30 }

๋ฐฐ์—ด ๋ณต์‚ฌํ•˜๊ธฐ

// 1. ์Šคํ”„๋ ˆ๋“œ ์—ฐ์‚ฐ์ž ์‚ฌ์šฉ
const numbers = [1, 2, 3];
const newNumbers = [...numbers, 4];  // [1, 2, 3, 4]

// 2. concat() ์‚ฌ์šฉ
const moreNumbers = numbers.concat(5);  // [1, 2, 3, 5]

// 3. slice() ์‚ฌ์šฉ
const numbersCopy = numbers.slice();  // [1, 2, 3]

โš ๏ธ ์ฃผ์˜: ์–•์€ ๋ณต์‚ฌ๋Š” ์ค‘์ฒฉ๋œ ๊ฐ์ฒด๋‚˜ ๋ฐฐ์—ด์—์„œ๋Š” ๋ถˆ๋ณ€์„ฑ์„ ๋ณด์žฅํ•˜์ง€ ์•Š์•„! 1๋‹จ๊ณ„ ๊นŠ์ด์—์„œ๋งŒ ์ƒˆ ์ฐธ์กฐ๊ฐ€ ์ƒ์„ฑ๋˜๊ณ , ๋‚ด๋ถ€ ๊ฐ์ฒด๋Š” ์—ฌ์ „ํžˆ ์›๋ณธ๊ณผ ๊ฐ™์€ ์ฐธ์กฐ๋ฅผ ๊ณต์œ ํ•˜๊ฒŒ ๋ผ.

2. ๊นŠ์€ ๋ณต์‚ฌ(Deep Copy) ๊ตฌํ˜„ํ•˜๊ธฐ ๐Ÿ”

// 1. JSON์„ ํ™œ์šฉํ•œ ๊นŠ์€ ๋ณต์‚ฌ (๊ฐ„๋‹จํ•˜์ง€๋งŒ ์ œํ•œ์ )
const original = { user: { name: '๋ฏผ์ˆ˜', hobbies: ['์ถ•๊ตฌ', '๊ฒŒ์ž„'] } };
const deepCopy = JSON.parse(JSON.stringify(original));

// 2. ๊ตฌ์กฐ๋ถ„ํ•ด์™€ ์Šคํ”„๋ ˆ๋“œ๋ฅผ ํ™œ์šฉํ•œ ์ˆ˜๋™ ๊นŠ์€ ๋ณต์‚ฌ
const manualDeepCopy = {
  ...original,
  user: {
    ...original.user,
    hobbies: [...original.user.hobbies]
  }
};

JSON ๋ฐฉ์‹์€ ํ•จ์ˆ˜, undefined, Symbol ๋“ฑ์„ ์ฒ˜๋ฆฌํ•˜์ง€ ๋ชปํ•˜๋Š” ํ•œ๊ณ„๊ฐ€ ์žˆ์–ด. ์‹ค๋ฌด์—์„œ๋Š” ๋ณดํ†ต ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, ํ•„์š”์— ๋งž๊ฒŒ ์ง์ ‘ ๊นŠ์€ ๋ณต์‚ฌ ํ•จ์ˆ˜๋ฅผ ๊ตฌํ˜„ํ•ด.

3. ๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ ๋‹ค๋ฃจ๊ธฐ ์œ„ํ•œ ํ•จ์ˆ˜ํ˜• ์ ‘๊ทผ๋ฒ• ๐Ÿง 

ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฐฉ์‹์„ ํ™œ์šฉํ•˜๋ฉด ๋ถˆ๋ณ€์„ฑ์„ ๋” ์šฐ์•„ํ•˜๊ฒŒ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์–ด:

// ๋ฐฐ์—ด์˜ ๋ถˆ๋ณ€์  ์กฐ์ž‘
const numbers = [1, 2, 3, 4, 5];

// โŒ ๊ฐ€๋ณ€์  ๋ฐฉ์‹ (์›๋ณธ ๋ณ€๊ฒฝ)
// numbers.push(6);
// numbers.pop();
// numbers[0] = 10;

// โœ… ๋ถˆ๋ณ€์  ๋ฐฉ์‹ (์ƒˆ ๋ฐฐ์—ด ๋ฐ˜ํ™˜)
const added = [...numbers, 6];        // ์ถ”๊ฐ€
const removed = numbers.slice(0, -1);  // ๋งˆ์ง€๋ง‰ ์š”์†Œ ์ œ๊ฑฐ
const replaced = [10, ...numbers.slice(1)];  // ์ฒซ ์š”์†Œ ๊ต์ฒด
const filtered = numbers.filter(n => n % 2 === 0);  // ์ง์ˆ˜๋งŒ ํ•„ํ„ฐ๋ง
const mapped = numbers.map(n => n * 2);  // ๋ชจ๋“  ์š”์†Œ 2๋ฐฐ๋กœ

๊ฐ์ฒด๋„ ๋น„์Šทํ•œ ํŒจํ„ด์œผ๋กœ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์–ด:

const user = {
  name: '์ง€๋ฏผ',
  age: 27,
  address: {
    city: '์„œ์šธ',
    district: '๊ฐ•๋‚จ๊ตฌ'
  }
};

// ๋ถˆ๋ณ€์ ์œผ๋กœ ์ค‘์ฒฉ ์†์„ฑ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ
const updatedUser = {
  ...user,
  age: 28,  // ๋‚˜์ด ์—…๋ฐ์ดํŠธ
  address: {
    ...user.address,
    district: '์†กํŒŒ๊ตฌ'  // ๊ตฌ๋งŒ ๋ณ€๊ฒฝ
  }
};
๋ถˆ๋ณ€์„ฑ ๊ตฌํ˜„ ๋ฐฉ์‹ ๋น„๊ต ๊ฐ€๋ณ€์  ๋ฐฉ์‹ array.push(item) obj.property = newValue ํŠน์ง• โœ“ ์ฝ”๋“œ๊ฐ€ ๊ฐ„๊ฒฐํ•จ โœ— ์›๋ณธ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ โœ— ๋ถ€์ž‘์šฉ ๋ฐœ์ƒ ๊ฐ€๋Šฅ์„ฑ ๋ถˆ๋ณ€์  ๋ฐฉ์‹ [...array, item] { ...obj, property: newValue } ํŠน์ง• โœ“ ์›๋ณธ ๋ฐ์ดํ„ฐ ๋ณด์กด โœ“ ์˜ˆ์ธก ๊ฐ€๋Šฅํ•œ ์ฝ”๋“œ โœ“ ๋ณ€๊ฒฝ ์ถ”์  ์šฉ์ด ๋ถˆ๋ณ€์„ฑ์€ ์ฝ”๋“œ์˜ ์•ˆ์ •์„ฑ๊ณผ ์˜ˆ์ธก ๊ฐ€๋Šฅ์„ฑ์„ ๋†’์—ฌ์ค๋‹ˆ๋‹ค

์ด๋Ÿฐ ํŒจํ„ด๋“ค์„ ์ตํžˆ๋ฉด ๋ณต์žกํ•œ ์ค‘์ฒฉ ๊ตฌ์กฐ์—์„œ๋„ ๋ถˆ๋ณ€์„ฑ์„ ์œ ์ง€ํ•˜๋ฉด์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์–ด. ํ•˜์ง€๋งŒ ๊ตฌ์กฐ๊ฐ€ ๊นŠ์–ด์งˆ์ˆ˜๋ก ์ฝ”๋“œ๊ฐ€ ๋ณต์žกํ•ด์ง€๋Š” ๋‹จ์ ์ด ์žˆ์ง€. ์ด๋Ÿด ๋•Œ ์˜์† ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋‚˜ ์ „๋ฌธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๋„์›€์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์–ด. ๋‹ค์Œ ์„น์…˜์—์„œ ์‚ดํŽด๋ณด์ž! ๐Ÿš€

4. ์˜์† ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ž€? ๐ŸŒณ

์˜์† ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ(Persistent Data Structures)๋Š” ๋ถˆ๋ณ€์„ฑ์„ ํšจ์œจ์ ์œผ๋กœ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•œ ํŠน๋ณ„ํ•œ ์ž๋ฃŒ๊ตฌ์กฐ์•ผ. "์˜์†์ "์ด๋ผ๋Š” ๋ง์€ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์˜ ์ด์ „ ๋ฒ„์ „์ด ํ•ญ์ƒ ๋ณด์กด๋œ๋‹ค๋Š” ์˜๋ฏธ์•ผ. ๐Ÿ•ฐ๏ธ

์˜์† ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์˜ ํ•ต์‹ฌ ๊ฐœ๋…

์ผ๋ฐ˜์ ์ธ ๋ถˆ๋ณ€ ๋ฐ์ดํ„ฐ ๊ตฌํ˜„์€ ๋ณ€๊ฒฝ์ด ํ•„์š”ํ•  ๋•Œ๋งˆ๋‹ค ์ „์ฒด ๋ฐ์ดํ„ฐ๋ฅผ ๋ณต์‚ฌํ•ด์•ผ ํ•ด์„œ ๋ฉ”๋ชจ๋ฆฌ์™€ ์„ฑ๋Šฅ ์ธก๋ฉด์—์„œ ๋น„ํšจ์œจ์ ์ผ ์ˆ˜ ์žˆ์–ด. ํ•˜์ง€๋งŒ ์˜์† ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋Š” ๊ตฌ์กฐ ๊ณต์œ (Structural Sharing)๋ผ๋Š” ๊ธฐ๋ฒ•์„ ์‚ฌ์šฉํ•ด ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด.

๊ตฌ์กฐ ๊ณต์œ ๋ž€ ์ƒˆ ๋ฒ„์ „์„ ๋งŒ๋“ค ๋•Œ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์€ ๋ถ€๋ถ„์€ ์›๋ณธ๊ณผ ๊ณต์œ ํ•˜๊ณ , ๋ณ€๊ฒฝ๋œ ๋ถ€๋ถ„๋งŒ ์ƒˆ๋กœ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ์‹์ด์•ผ. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์„ ํฌ๊ฒŒ ์ค„์ด๋ฉด์„œ๋„ ๋ถˆ๋ณ€์„ฑ์˜ ์ด์ ์„ ๋ˆ„๋ฆด ์ˆ˜ ์žˆ์ง€!

ํŠธ๋ผ์ด(Trie)์™€ ๊ฐ™์€ ์˜์† ์ž๋ฃŒ๊ตฌ์กฐ ์ดํ•ดํ•˜๊ธฐ ๐ŸŒฒ

์˜์† ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์˜ ๋Œ€ํ‘œ์ ์ธ ์˜ˆ๋กœ๋Š” ํŠธ๋ผ์ด(Trie), ํŠนํžˆ ๊ทธ ๋ณ€ํ˜•์ธ HAMT(Hash Array Mapped Trie)๊ฐ€ ์žˆ์–ด. ์ด ๊ตฌ์กฐ๋“ค์€ Immutable.js๋‚˜ Immer ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๊ธฐ๋ฐ˜์ด ๋˜๋Š” ๊ฐœ๋…์ด์•ผ.

๊ตฌ์กฐ ๊ณต์œ (Structural Sharing) ๊ฐœ๋… A B C D E F G ์›๋ณธ ํŠธ๋ฆฌ A' B' C D' E F G ๋ณ€๊ฒฝ๋œ ํŠธ๋ฆฌ ๋ณ€๊ฒฝ๋œ ๋…ธ๋“œ(์ดˆ๋ก์ƒ‰)๋งŒ ์ƒˆ๋กœ ์ƒ์„ฑํ•˜๊ณ  ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์€ ๋ถ€๋ถ„(ํŒŒ๋ž€์ƒ‰)์€ ์›๋ณธ๊ณผ ๊ณต์œ 

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

์˜์† ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์˜ ์žฅ์  ๐ŸŒŸ

  1. ํšจ์œจ์ ์ธ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ: ๋ณ€๊ฒฝ๋œ ๋ถ€๋ถ„๋งŒ ์ƒˆ๋กœ ์ƒ์„ฑํ•˜๋ฏ€๋กœ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์ด ์ตœ์†Œํ™”๋ผ.
  2. ๋น ๋ฅธ ๋น„๊ต ์—ฐ์‚ฐ: ์ฐธ์กฐ ๋™๋“ฑ์„ฑ ๊ฒ€์‚ฌ๋งŒ์œผ๋กœ ๋ณ€๊ฒฝ ์—ฌ๋ถ€๋ฅผ ๋น ๋ฅด๊ฒŒ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด.
  3. ์‹œ๊ฐ„ ์—ฌํ–‰(Time Travel): ๋ชจ๋“  ์ด์ „ ๋ฒ„์ „์ด ๋ณด์กด๋˜๋ฏ€๋กœ ์ƒํƒœ ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ์‰ฝ๊ฒŒ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด.
  4. ํŠธ๋žœ์žญ์…˜ ์•ˆ์ „์„ฑ: ์ž‘์—… ์ค‘๊ฐ„์— ์‹คํŒจํ•ด๋„ ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ์ด ์œ ์ง€๋ผ.
  5. ๋™์‹œ์„ฑ ์ง€์›: ์—ฌ๋Ÿฌ ์ž‘์—…์ด ๋™์‹œ์— ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์–ด.

์‹ค์ œ ์˜ˆ: Immutable.js์˜ List ์‚ฌ์šฉ ์˜ˆ์ œ

import { List } from 'immutable';

// ๋ถˆ๋ณ€ ๋ฆฌ์ŠคํŠธ ์ƒ์„ฑ
const list1 = List([1, 2, 3, 4]);

// ์ƒˆ ๋ฆฌ์ŠคํŠธ ์ƒ์„ฑ (์›๋ณธ์€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์Œ)
const list2 = list1.push(5);
const list3 = list1.set(0, 10);

console.log(list1.toArray());  // [1, 2, 3, 4] - ์›๋ณธ ์œ ์ง€
console.log(list2.toArray());  // [1, 2, 3, 4, 5]
console.log(list3.toArray());  // [10, 2, 3, 4]

// ํšจ์œจ์ ์ธ ๋น„๊ต
console.log(list1 === list2);  // false - ๋‹ค๋ฅธ ๊ฐ์ฒด
console.log(list1.equals(list2));  // false - ๋‚ด์šฉ์ด ๋‹ค๋ฆ„

์˜์† ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋Š” ์žฌ๋Šฅ๋„ท์—์„œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ด€๋ จ ์žฌ๋Šฅ์„ ๊ณต์œ ํ•˜๋Š” ๊ฐœ๋ฐœ์ž๋“ค ์‚ฌ์ด์—์„œ๋„ ์ธ๊ธฐ ์žˆ๋Š” ์ฃผ์ œ์•ผ. ํŠนํžˆ ๋Œ€๊ทœ๋ชจ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ฐœ๋ฐœํ•  ๋•Œ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๊ฑฐ๋“ . ๋‹ค์Œ ์„น์…˜์—์„œ๋Š” ์ด๋Ÿฐ ๊ฐœ๋…์„ ์‹ค์ œ๋กœ ๊ตฌํ˜„ํ•œ ์ธ๊ธฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์„ ์‚ดํŽด๋ณผ๊ฒŒ! ๐Ÿ“š