[TECH-QA] ์›์‹œ๊ฐ’๊ณผ ์ฐธ์กฐํ˜•

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ์›์‹œ๊ฐ’(Primitive Values)๊ณผ ์ฐธ์กฐํ˜•(Reference Types)์˜ ๋™์ž‘ ๋ฐฉ์‹, ๊ทธ๋ฆฌ๊ณ  ์ด๋“ค์˜ ๋ถˆ๋ณ€์„ฑ(Immutability)๊ณผ ๊ฐ€๋ณ€์„ฑ(Mutability)์— ๋Œ€ํ•ด ์„ค๋ช… ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์›์‹œ๊ฐ’ (Primitive Values)

  • ์ •์˜ : ์›์‹œ๊ฐ’์€ ๋” ์ด์ƒ ์ชผ๊ฐค ์ˆ˜ ์—†๋Š” ๊ธฐ๋ณธ ๋ฐ์ดํ„ฐ ํƒ€์ž…์œผ๋กœ, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ๋Š” undefined, null, boolean, number, bigint, string, symbol์ด ์ด์— ํ•ด๋‹นํ•ฉ๋‹ˆ๋‹ค.
  • ๋ณต์ œ ๋ฐฉ์‹ : ๋‹ค๋ฅธ ๋ณ€์ˆ˜์— ์›์‹œ๊ฐ’์„ ๋ณต์‚ฌํ•  ๋•Œ, ํ•ด๋‹น ๊ฐ’์ด ๊ทธ๋Œ€๋กœ ๋ณต์‚ฌ๋˜์–ด ์ƒˆ๋กœ์šด ๋ณ€์ˆ˜์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ์ด๋•Œ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ ๊ฐœ๋…์€ ๊ด€์—ฌํ•˜์ง€ ์•Š๊ณ , ๋‹จ์ˆœํžˆ ๊ฐ’๋งŒ ๋ณต์‚ฌ๋ฉ๋‹ˆ๋‹ค.
  • ๋ถˆ๋ณ€์„ฑ : ์›์‹œ๊ฐ’์€ ๋ถˆ๋ณ€๊ฐ’์ž…๋‹ˆ๋‹ค. ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋ ค๊ณ  ํ•˜๋ฉด, ๊ธฐ์กด ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„์˜ ๊ฐ’์„ ์ˆ˜์ •ํ•˜๋Š” ๋Œ€์‹  ์ƒˆ๋กœ์šด ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„์— ์ƒˆ๋กœ์šด ๊ฐ’์„ ์ €์žฅํ•˜๊ณ , ๋ณ€์ˆ˜๊ฐ€ ์ฐธ์กฐํ•˜๋˜ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๋ฅผ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.
let a = 10; // a๋Š” ์ˆซ์ž 10์„ ์ €์žฅ
let b = a;  // b์— a์˜ ๊ฐ’ 10์„ ๋ณต์‚ฌ (๋…๋ฆฝ์ ์ธ ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„)

console.log(a); // 10
console.log(b); // 10

b = 20; // b์— ์ƒˆ๋กœ์šด ๊ฐ’ 20์„ ํ• ๋‹น (์ƒˆ๋กœ์šด ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„ ์‚ฌ์šฉ)
console.log(a); // 10 (a๋Š” ์—ฌ์ „ํžˆ 10์„ ์ฐธ์กฐ)
console.log(b); // 20
  • b = a๋ฅผ ์‹คํ–‰ํ•˜๋ฉด, a์˜ ๊ฐ’ 10์ด ๋ณต์‚ฌ๋˜์–ด b์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ์ด๋•Œ a์™€ b๋Š” ๊ฐ๊ฐ ๋…๋ฆฝ์ ์ธ ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„์„ ๊ฐ€๋ฆฌํ‚ต๋‹ˆ๋‹ค.
  • b = 20์œผ๋กœ ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋ฉด, b๋Š” ์ƒˆ๋กœ์šด ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„์— 20์„ ์ €์žฅํ•˜๊ณ  ๊ทธ ์ฃผ์†Œ๋ฅผ ์ฐธ์กฐํ•ฉ๋‹ˆ๋‹ค. a๋Š” ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ์›์‹œ๊ฐ’์€ ๋ถˆ๋ณ€์ ์ด๋ฏ€๋กœ, a๋‚˜ b์˜ ๊ฐ’์„ ์ง์ ‘ ์ˆ˜์ •ํ•˜๋Š” ๋Œ€์‹  ์ƒˆ๋กœ์šด ๊ฐ’์„ ๋ฉ”๋ชจ๋ฆฌ์— ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค.

์ฐธ์กฐํ˜• (Reference Types)

  • ์ •์˜ : ์ฐธ์กฐํ˜•์€ ๊ฐ์ฒด(Object), ๋ฐฐ์—ด(Array), ํ•จ์ˆ˜(Function) ๋“ฑ์œผ๋กœ, ์—ฌ๋Ÿฌ ๊ฐ’์„ ํ•˜๋‚˜์˜ ๋‹จ์œ„๋กœ ๋ฌถ์€ ๋ฐ์ดํ„ฐ ํƒ€์ž…์ž…๋‹ˆ๋‹ค.
  • ๋ณต์ œ ๋ฐฉ์‹ : ์ฐธ์กฐํ˜•์€ ๊ฐ’์ด ๋‹ด๊ธด ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๋ฅผ ๋ณต์ œํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ๋ณ€์ˆ˜๋Š” ์‹ค์ œ ๋ฐ์ดํ„ฐ(๊ฐ์ฒด)๊ฐ€ ์ €์žฅ๋œ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋ฉฐ, ๋ณต์‚ฌ ์‹œ ์ด ์ฃผ์†Œ๋ฅผ ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋ณต์‚ฌ๋œ ๋ณ€์ˆ˜๋Š” ์›๋ณธ ๊ฐ์ฒด์™€ ๋™์ผํ•œ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๋ฅผ ์ฐธ์กฐํ•ฉ๋‹ˆ๋‹ค.
  • ๊ฐ€๋ณ€์„ฑ : ์ฐธ์กฐํ˜•์€ ๊ฐ€๋ณ€๊ฐ’์ž…๋‹ˆ๋‹ค. ๊ฐ์ฒด๊ฐ€ ์ €์žฅ๋œ ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„์€ ์ˆ˜์ • ๊ฐ€๋Šฅํ•˜๋ฉฐ, ๋ณ€์ˆ˜๋Š” ํ•ด๋‹น ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ์ฒด์˜ ํ”„๋กœํผํ‹ฐ๋ฅผ ์ถ”๊ฐ€, ๊ฐฑ์‹ , ์‚ญ์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
let obj1 = { name: "Alice", age: 25 }; // obj1์€ ๊ฐ์ฒด์˜ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๋ฅผ ์ฐธ์กฐ
let obj2 = obj1; // obj2๋Š” obj1๊ณผ ๋™์ผํ•œ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๋ฅผ ์ฐธ์กฐ

console.log(obj1); // { name: "Alice", age: 25 }
console.log(obj2); // { name: "Alice", age: 25 }

// obj2๋ฅผ ํ†ตํ•ด ๊ฐ์ฒด ์ˆ˜์ •
obj2.age = 30;
obj2.city = "Seoul"; // ๋™์ ์œผ๋กœ ํ”„๋กœํผํ‹ฐ ์ถ”๊ฐ€

console.log(obj1); // { name: "Alice", age: 30, city: "Seoul" }
console.log(obj2); // { name: "Alice", age: 30, city: "Seoul" }

// ํ”„๋กœํผํ‹ฐ ์‚ญ์ œ
delete obj1.city;

console.log(obj1); // { name: "Alice", age: 30 }
console.log(obj2); // { name: "Alice", age: 30 }
  • obj2 = obj1์€ obj1์ด ์ฐธ์กฐํ•˜๋Š” ๊ฐ์ฒด์˜ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๋ฅผ obj2์— ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ obj1๊ณผ obj2๋Š” ๋™์ผํ•œ ๊ฐ์ฒด๋ฅผ ๊ฐ€๋ฆฌํ‚ต๋‹ˆ๋‹ค.
  • obj2.age = 30 ๋˜๋Š” obj2.city = "Seoul"๋กœ ๊ฐ์ฒด๋ฅผ ์ˆ˜์ •ํ•˜๋ฉด, ๋™์ผํ•œ ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„์„ ์ฐธ์กฐํ•˜๋Š” obj1์—๋„ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค.
  • ์ฐธ์กฐํ˜•์€ ๊ฐ€๋ณ€์ ์ด๋ฏ€๋กœ, ๊ฐ์ฒด์˜ ํ”„๋กœํผํ‹ฐ๋ฅผ ๋™์ ์œผ๋กœ ์ถ”๊ฐ€(city), ๊ฐฑ์‹ (age), ์‚ญ์ œ(delete obj1.city)ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์›์‹œ๊ฐ’ vs ์ฐธ์กฐํ˜• ๋น„๊ต

ํŠน์ง•
์›์‹œ๊ฐ’ (Primitive)
์ฐธ์กฐํ˜• (Reference)
๋ฐ์ดํ„ฐ ํƒ€์ž…number, string, boolean ๋“ฑobject, array, function ๋“ฑ
๋ณต์ œ ๋ฐฉ์‹๊ฐ’ ์ž์ฒด ๋ณต์‚ฌ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ ๋ณต์‚ฌ
๋ณ€๊ฒฝ ๊ฐ€๋Šฅ์„ฑ๋ถˆ๋ณ€ (์ƒˆ๋กœ์šด ๊ฐ’์œผ๋กœ ๊ต์ฒด)๊ฐ€๋ณ€ (๊ฐ์ฒด ๋‚ด๋ถ€ ์ˆ˜์ • ๊ฐ€๋Šฅ)
๋ฉ”๋ชจ๋ฆฌ ๋™์ž‘์ƒˆ๋กœ์šด ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„ ์ƒ์„ฑ๋™์ผํ•œ ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„ ๊ณต์œ 
์˜ˆ์‹œlet x = 5; x = 10;let obj = {a: 1}; obj.a = 2;

์ฐธ์กฐํ˜•์˜ ๊นŠ์€ ๋ณต์‚ฌ(Deep Copy)

์ฐธ์กฐํ˜•์˜ ๊ธฐ๋ณธ ๋ณต์‚ฌ๋Š” ์–•์€ ๋ณต์‚ฌ(Shallow Copy)๋กœ, ๋™์ผํ•œ ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ฐ์ฒด๋ฅผ ๋…๋ฆฝ์ ์œผ๋กœ ๋ณต์‚ฌํ•˜๋ ค๋ฉด ๊นŠ์€ ๋ณต์‚ฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“ ์–•์€ ๋ณต์‚ฌ vs ๊นŠ์€ ๋ณต์‚ฌ

// ์–•์€ ๋ณต์‚ฌ
let original = { name: "Bob", info: { age: 40 } };
let shallowCopy = Object.assign({}, original);

shallowCopy.name = "Charlie";
shallowCopy.info.age = 50;

console.log(original); // { name: "Bob", info: { age: 50 } } (info๋Š” ์—ฌ์ „ํžˆ ๊ณต์œ ๋จ)
console.log(shallowCopy); // { name: "Charlie", info: { age: 50 } }

// ๊นŠ์€ ๋ณต์‚ฌ
let deepCopy = JSON.parse(JSON.stringify(original));

deepCopy.name = "David";
deepCopy.info.age = 60;

console.log(original); // { name: "Bob", info: { age: 50 } } (๋ณ€๊ฒฝ๋˜์ง€ ์•Š์Œ)
console.log(deepCopy); // { name: "David", info: { age: 60 } }
  • ์–•์€ ๋ณต์‚ฌ(Object.assign)๋Š” ์ตœ์ƒ์œ„ ํ”„๋กœํผํ‹ฐ๋งŒ ๋ณต์‚ฌํ•˜๊ณ , ์ค‘์ฒฉ ๊ฐ์ฒด(info)๋Š” ์—ฌ์ „ํžˆ ์›๋ณธ๊ณผ ๊ณต์œ ๋ฉ๋‹ˆ๋‹ค.
  • ๊นŠ์€ ๋ณต์‚ฌ(JSON.parse(JSON.stringify()))๋Š” ๊ฐ์ฒด ์ „์ฒด๋ฅผ ์ƒˆ๋กœ ์ƒ์„ฑํ•˜์—ฌ ๋…๋ฆฝ์ ์ธ ๋ณต์‚ฌ๋ณธ์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

๐Ÿ“ ์›์‹œ๊ฐ’ ํ™œ์šฉ

function updateScore(score) {
    score = score + 10; // ์ƒˆ๋กœ์šด ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„์— ๊ฐ’์„ ์ €์žฅ
    return score;
}

let playerScore = 50;
console.log(updateScore(playerScore)); // 60
console.log(playerScore); // 50 (์›๋ณธ ๊ฐ’์€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์Œ)
์›์‹œ๊ฐ’์€ ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ ์ˆ˜์ •ํ•ด๋„ ์›๋ณธ์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๐Ÿ“ ์ฐธ์กฐํ˜• ํ™œ์šฉ

function updateProfile(profile) {
    profile.age += 1; // ๋™์ผํ•œ ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ์ˆ˜์ •
    profile.city = "Busan"; // ๋™์  ํ”„๋กœํผํ‹ฐ ์ถ”๊ฐ€
}

let user = { name: "Eve", age: 28 };
updateProfile(user);

console.log(user); // { name: "Eve", age: 29, city: "Busan" }
์ฐธ์กฐํ˜•์€ ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ ์ˆ˜์ •ํ•˜๋ฉด ์›๋ณธ ๊ฐ์ฒด๊ฐ€ ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค. user ์›๋ณธ ๊ฐ์ฒด๊ฐ€ { name: "Eve", age: 28 } ์—์„œ { name: "Eve", age: 29, city: "Busan" } ์œผ๋กœ ๋ณ€๊ฒฝ ๋จ
  • ์›์‹œ๊ฐ’: ๊ฐ’ ์ž์ฒด๋ฅผ ๋ณต์‚ฌ, ๋ถˆ๋ณ€, ์žฌํ• ๋‹น ์‹œ ์ƒˆ๋กœ์šด ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„ ์‚ฌ์šฉ.
  • ์ฐธ์กฐํ˜•: ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๋ฅผ ๋ณต์‚ฌ, ๊ฐ€๋ณ€, ๊ฐ์ฒด ๋‚ด๋ถ€๋ฅผ ์ง์ ‘ ์ˆ˜์ • ๊ฐ€๋Šฅ (ํ”„๋กœํผํ‹ฐ ์ถ”๊ฐ€/๊ฐฑ์‹ /์‚ญ์ œ).
  • ์›์‹œ๊ฐ’์€ ๋…๋ฆฝ์ ์ด๊ณ  ์•ˆ์ „ํ•˜์ง€๋งŒ ์ˆ˜์ •์ด ์ œํ•œ์ ์ด๋ฉฐ, ์ฐธ์กฐํ˜•์€ ์œ ์—ฐํ•˜์ง€๋งŒ ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ๋กœ ์ธํ•ด ์ฃผ์˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
  • ๊นŠ์€ ๋ณต์‚ฌ๋ฅผ ํ†ตํ•ด ์ฐธ์กฐํ˜•์˜ ๋…๋ฆฝ์ ์ธ ๋ณต์‚ฌ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ ๊นŠ์€ ๋ณต์‚ฌ(Deep Copy)๋ฅผ ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์—ฌ๋Ÿฌ ๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ƒํ™ฉ์— ๋”ฐ๋ผ ๊ฐ„๋‹จํ•œ ๊ฐ์ฒด๋ถ€ํ„ฐ ๋ณต์žกํ•œ ๊ฐ์ฒด๊นŒ์ง€ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜์— ๋Œ€ํ‘œ์ ์ธ ๋ฐฉ๋ฒ•๊ณผ ์˜ˆ์ œ๋ฅผ ์ •๋ฆฌํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
๊ตฌ์กฐ์  ๋ณต์ œ (Structured Clone)
structuredClone()์€ ๋ธŒ๋ผ์šฐ์ € ํ™˜๊ฒฝ(Node.js 17+ ๋˜๋Š” ์ตœ์‹  ๋ธŒ๋ผ์šฐ์ €)์—์„œ ์ œ๊ณต๋˜๋Š” ๊นŠ์€ ๋ณต์‚ฌ๋ฅผ ์œ„ํ•œ ๋„ค์ดํ‹ฐ๋ธŒ ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค. ์ด ๋ฐฉ๋ฒ•์€ JSON ๋ฐฉ์‹๋ณด๋‹ค ๋” ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ ํƒ€์ž…(์˜ˆ: Date, Map, Set ๋“ฑ)์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
// ์›๋ณธ ๊ฐ์ฒด
let original = {
  name: "Bob",
  info: { age: 50, date: new Date() },
  hobbies: ["reading", "gaming"]
};

// ๊นŠ์€ ๋ณต์‚ฌ (structuredClone ์‚ฌ์šฉ)
let deepCopy = structuredClone(original);

// ๋ณต์‚ฌ๋ณธ ์ˆ˜์ •
deepCopy.name = "David";
deepCopy.info.age = 60;
deepCopy.hobbies.push("coding");

console.log(original);
// ์ถœ๋ ฅ: { name: "Bob", info: { age: 50, date: [Date] }, hobbies: ["reading", "gaming"] }

console.log(deepCopy);
// ์ถœ๋ ฅ: { name: "David", info: { age: 60, date: [Date] }, hobbies: ["reading", "gaming", "coding"] }
  • structuredClone()์€ ๊ฐ์ฒด์™€ ๊ทธ ํ•˜์œ„ ๊ตฌ์กฐ๋ฅผ ์™„์ „ํžˆ ๋…๋ฆฝ์ ์œผ๋กœ ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค.
  • ์›๋ณธ ๊ฐ์ฒด์˜ info.age๋‚˜ hobbies ๋ฐฐ์—ด์ด ์ˆ˜์ •๋˜์ง€ ์•Š์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • Date ๊ฐ์ฒด๋„ ๋ณ„๋„์˜ ์ธ์Šคํ„ด์Šค๋กœ ๋ณต์‚ฌ๋˜๋ฉฐ, JSON ๋ฐฉ์‹๊ณผ ๋‹ฌ๋ฆฌ ์†์‹ค ์—†์ด ๋ณต์‚ฌ๋ฉ๋‹ˆ๋‹ค.

๐Ÿ“ ์žฌ๊ท€ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•œ ๊นŠ์€ ๋ณต์‚ฌ

์ง์ ‘ ๊นŠ์€ ๋ณต์‚ฌ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ, ๊ฐ์ฒด์™€ ๋ฐฐ์—ด์„ ์žฌ๊ท€์ ์œผ๋กœ ์ˆœํšŒํ•˜๋ฉฐ ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฐฉ๋ฒ•์€ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•์ด ๊ฐ€๋Šฅํ•˜๊ณ , ํŠน์ • ๋ฐ์ดํ„ฐ ํƒ€์ž…์„ ์ถ”๊ฐ€๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
// ๊นŠ์€ ๋ณต์‚ฌ ํ•จ์ˆ˜
function deepCopy(obj) {
  // ์›์‹œ๊ฐ’์ด๊ฑฐ๋‚˜ null์ธ ๊ฒฝ์šฐ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜
  if (obj === null || typeof obj !== "object") {
    return obj;
  }

  // ๋ฐฐ์—ด์ธ ๊ฒฝ์šฐ
  if (Array.isArray(obj)) {
    return obj.map(item => deepCopy(item));
  }

  // ๊ฐ์ฒด์ธ ๊ฒฝ์šฐ
  const copy = {};
  for (const key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      copy[key] = deepCopy(obj[key]);
    }
  }
  return copy;
}

// ์›๋ณธ ๊ฐ์ฒด
let original = {
  name: "Bob",
  info: { age: 50 },
  hobbies: ["reading", "gaming"]
};

// ๊นŠ์€ ๋ณต์‚ฌ
let deepCopy = deepCopy(original);

// ๋ณต์‚ฌ๋ณธ ์ˆ˜์ •
deepCopy.name = "David";
deepCopy.info.age = 60;
deepCopy.hobbies.push("coding");

console.log(original);
// ์ถœ๋ ฅ: { name: "Bob", info: { age: 50 }, hobbies: ["reading", "gaming"] }

console.log(deepCopy);
// ์ถœ๋ ฅ: { name: "David", info: { age: 60 }, hobbies: ["reading", "gaming", "coding"] }
  • deepCopy ํ•จ์ˆ˜๋Š” ์žฌ๊ท€์ ์œผ๋กœ ๊ฐ์ฒด์˜ ๋ชจ๋“  ํ”„๋กœํผํ‹ฐ๋ฅผ ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค.
  • ๋ฐฐ์—ด๊ณผ ๊ฐ์ฒด๋ฅผ ๊ตฌ๋ถ„ํ•˜์—ฌ ์ฒ˜๋ฆฌํ•˜๋ฉฐ, ์›์‹œ๊ฐ’์€ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  • ์ด ๋ฐฉ์‹์€ JSON ๋ฐฉ์‹์˜ ํ•œ๊ณ„๋ฅผ ๊ทน๋ณตํ•˜๊ณ , ํ•จ์ˆ˜๋‚˜ undefined ๊ฐ™์€ ๊ฐ’๋„ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•ํ•˜์—ฌ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(ํ•„์š” ์‹œ ์ถ”๊ฐ€ ๋กœ์ง ๊ตฌํ˜„ ๊ฐ€๋Šฅ).

๐Ÿ“ Lodash ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ _.cloneDeep

์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” Lodash ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค. Lodash์˜ _.cloneDeep ๋ฉ”์„œ๋“œ๋Š” ๊นŠ์€ ๋ณต์‚ฌ๋ฅผ ๊ฐ„ํŽธํ•˜๊ฒŒ ์ˆ˜ํ–‰ํ•˜๋ฉฐ, ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ ํƒ€์ž…์„ ์•ˆ์ „ํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
// Lodash๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š” (HTML์—์„œ๋Š” CDN ๋˜๋Š” ๋ชจ๋“ˆ๋กœ ์ž„ํฌํŠธ)
// ์˜ˆ: <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>

// ์›๋ณธ ๊ฐ์ฒด
let original = {
  name: "Bob",
  info: { age: 50 },
  hobbies: ["reading", "gaming"]
};

// ๊นŠ์€ ๋ณต์‚ฌ (Lodash ์‚ฌ์šฉ)
let deepCopy = _.cloneDeep(original);

// ๋ณต์‚ฌ๋ณธ ์ˆ˜์ •
deepCopy.name = "David";
deepCopy.info.age = 60;
deepCopy.hobbies.push("coding");

console.log(original);
// ์ถœ๋ ฅ: { name: "Bob", info: { age: 50 }, hobbies: ["reading", "gaming"] }

console.log(deepCopy);
// ์ถœ๋ ฅ: { name: "David", info: { age: 60 }, hobbies: ["reading", "gaming", "coding"] }
  • _.cloneDeep์€ Lodash ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๊นŠ์€ ๋ณต์‚ฌ ๋ฉ”์„œ๋“œ๋กœ, ๋ณต์žกํ•œ ๊ฐ์ฒด ๊ตฌ์กฐ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค.
  • JSON ๋ฐฉ์‹๋ณด๋‹ค ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ ํƒ€์ž…(์˜ˆ: ํ•จ์ˆ˜, RegExp, Map)์„ ์ง€์›ํ•˜๋ฉฐ, ์„ฑ๋Šฅ๋„ ์ตœ์ ํ™”๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค.
๋ฐฉ๋ฒ•
์žฅ์ 
๋‹จ์ 
JSON.parse(JSON.stringify())๊ฐ„๋‹จํ•˜๊ณ  ๋ณ„๋„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ถˆํ•„์š”undefined, ํ•จ์ˆ˜, Date ๋“ฑ ์ผ๋ถ€ ๋ฐ์ดํ„ฐ ํƒ€์ž… ์†์‹ค ๊ฐ€๋Šฅ
structuredClone()๋„ค์ดํ‹ฐ๋ธŒ API, ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ ํƒ€์ž… ์ง€์›, ์†์‹ค ์—†์Œ๋ธŒ๋ผ์šฐ์ €/Node.js ๋ฒ„์ „ ์˜์กด์„ฑ (๊ตฌํ˜• ํ™˜๊ฒฝ์—์„œ ๋™์ž‘ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Œ)
์žฌ๊ท€ ํ•จ์ˆ˜ (deepCopy)์ปค์Šคํ„ฐ๋งˆ์ด์ง• ๊ฐ€๋Šฅ, ๋ชจ๋“  ๋ฐ์ดํ„ฐ ํƒ€์ž… ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ์ง์ ‘ ๊ตฌํ˜„ํ•ด์•ผ ํ•˜๋ฉฐ, ๋ณต์žกํ•œ ๊ฐ์ฒด์—์„œ ์„ฑ๋Šฅ ๊ณ ๋ ค ํ•„์š”
Lodash _.cloneDeep์‹ ๋ขฐ์„ฑ ๋†’์Œ, ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ ํƒ€์ž… ์ง€์›, ํ…Œ์ŠคํŠธ ์™„๋ฃŒ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์˜์กด์„ฑ ์ถ”๊ฐ€ ํ•„์š”

๐Ÿ“ ๋ณต์žกํ•œ ๊ฐ์ฒด(์ค‘์ฒฉ ๊ฐ์ฒด, ๋ฐฐ์—ด, Date, Map ํฌํ•จ)๋ฅผ ์‚ฌ์šฉํ•œ ์˜ˆ์ œ

// ์›๋ณธ ๊ฐ์ฒด (๋ณต์žกํ•œ ๊ตฌ์กฐ)
let original = {
  name: "Bob",
  info: {
    age: 50,
    birthday: new Date("1975-01-01"),
    address: { city: "Seoul", country: "Korea" }
  },
  hobbies: ["reading", { type: "gaming", level: "pro" }],
  metadata: new Map([["id", 123], ["active", true]])
};

// ๊นŠ์€ ๋ณต์‚ฌ
let deepCopy = structuredClone(original);

// ๋ณต์‚ฌ๋ณธ ์ˆ˜์ •
deepCopy.name = "David";
deepCopy.info.age = 60;
deepCopy.info.address.city = "Busan";
deepCopy.hobbies[1].level = "expert";
deepCopy.metadata.set("id", 456);

console.log(original);
// ์ถœ๋ ฅ: {
//   name: "Bob",
//   info: { age: 50, birthday: [Date 1975-01-01], address: { city: "Seoul", country: "Korea" } },
//   hobbies: ["reading", { type: "gaming", level: "pro" }],
//   metadata: Map { "id" => 123, "active" => true }
// }

console.log(deepCopy);
// ์ถœ๋ ฅ: {
//   name: "David",
//   info: { age: 60, birthday: [Date 1975-01-01], address: { city: "Busan", country: "Korea" } },
//   hobbies: ["reading", { type: "gaming", level: "expert" }],
//   metadata: Map { "id" => 456, "active" => true }
// }
  • ์ค‘์ฒฉ ๊ฐ์ฒด(address), ๋ฐฐ์—ด ๋‚ด ๊ฐ์ฒด(hobbies[1]), Date, Map ๋“ฑ ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ ํƒ€์ž…์ด ํฌํ•จ๋œ ๋ณต์žกํ•œ ๊ฐ์ฒด๋ฅผ ๋ณต์‚ฌํ–ˆ์Šต๋‹ˆ๋‹ค.
  • structuredClone์€ ๋ชจ๋“  ์ˆ˜์ค€์—์„œ ๋…๋ฆฝ์ ์ธ ๋ณต์‚ฌ๋ณธ์„ ์ƒ์„ฑํ•˜์—ฌ ์›๋ณธ์ด ์ˆ˜์ •๋˜์ง€ ์•Š์Œ์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.