JavaScript์ ์์ ๋ณต์ฌ์ ๊น์ ๋ณต์ฌ ์ฐจ์ด๋ฅผ ์ฝ๊ฒ ์ ๋ฆฌํ์ต๋๋ค.
JavaScript๋ก ๊ฐ๋ฐํ๋ค ๋ณด๋ฉด ๊ฐ์ฒด๋ฅผ ๋ณต์ฌํด์ผ ํ๋ ์ํฉ์ด ์์ฃผ ์๊น๋๋ค. ํ์ง๋ง ๋จ์ํ = ์ฐ์ฐ์๋ก ๋ณต์ฌํ๋ฉด ์์์น ๋ชปํ ๋ฒ๊ทธ๊ฐ ๋ฐ์ํ ์ ์์ด์. ์๋ํ๋ฉด ๊ฐ์ฒด๋ ์ฐธ์กฐ ํ์
์ด๊ธฐ ๋๋ฌธ์ด์ฃ .
์ด๋ฒ ๊ธ์์๋ ์์ ๋ณต์ฌ์ ๊น์ ๋ณต์ฌ์ ์ฐจ์ด์ , ๊ฐ๊ฐ์ ์ฌ์ฉ ์ฌ๋ก, ๊ทธ๋ฆฌ๊ณ ์ค๋ฌด์์ ๋ฐ๋ก ํ์ฉํ ์ ์๋ ๊ตฌํ ๋ฐฉ๋ฒ์ ์์๋ณด๊ฒ ์ต๋๋ค.
์์ ๋ณต์ฌ(Shallow Copy)๋?
์์ ๋ณต์ฌ๋ ๊ฐ์ฒด์ ์ต์์ ์์ฑ๋ง ๋ณต์ฌํ๋ ๋ฐฉ์์ ๋๋ค. ๋ณต์ฌ๋ ๊ฐ์ฒด์ ์๋ณธ ๊ฐ์ฒด๊ฐ ๊ฐ์ ์ฐธ์กฐ๋ฅผ ๊ณต์ ํ๊ฒ ๋์ด, ์ค์ฒฉ๋ ๊ฐ์ฒด๋ฅผ ์์ ํ๋ฉด ์๋ณธ์๋ ์ํฅ์ ๋ฏธ์นฉ๋๋ค.
์์ ๋ณต์ฌ์ ๋์ ์๋ฆฌ
์์ ๋ณต์ฌ๋ฅผ ์ํํ๋ฉด ์๋ก์ด ๊ฐ์ฒด๊ฐ ์์ฑ๋์ง๋ง(o1 !== o2), ๋ด๋ถ์ ์ค์ฒฉ๋ ๊ฐ์ฒด๋ ๋ฐฐ์ด์ ๊ฐ์ ๋ฉ๋ชจ๋ฆฌ ์ฃผ์๋ฅผ ๊ฐ๋ฆฌํต๋๋ค.
const original = {
name: "ํ๊ธธ๋",
age: 30,
address: {
city: "์์ธ",
district: "๊ฐ๋จ๊ตฌ"
}
};
const shallowCopy = Object.assign({}, original);
// ์ต์์ ์์ฑ ๋ณ๊ฒฝ - ์๋ณธ์ ์ํฅ ์์
shallowCopy.name = "๊น์ฒ ์";
console.log(original.name); // "ํ๊ธธ๋"
// ์ค์ฒฉ๋ ๊ฐ์ฒด ๋ณ๊ฒฝ - ์๋ณธ์๋ ์ํฅ!
shallowCopy.address.city = "๋ถ์ฐ";
console.log(original.address.city); // "๋ถ์ฐ"
์์ ๋ณต์ฌ ๊ตฌํ ๋ฐฉ๋ฒ
JavaScript์์ ์์ ๋ณต์ฌ๋ฅผ ์ํํ๋ ๋ฐฉ๋ฒ์ ์ฌ๋ฌ ๊ฐ์ง๊ฐ ์์ต๋๋ค:
1. Object.assign() ์ฌ์ฉ
const user = {
name: "๊น๋ฏผ์",
role: "๊ฐ๋ฐ์"
};
const clone = Object.assign({}, user);
2. ์ ๊ฐ ๊ตฌ๋ฌธ(Spread Syntax) ์ฌ์ฉ
const user = {
name: "๋ฐ์ง์",
role: "๋์์ด๋"
};
const clone = { ...user };
3. ๋ฐฐ์ด์ ๊ฒฝ์ฐ
const items = [1, 2, { value: 3 }];
// Array.from()
const copy1 = Array.from(items);
// slice()
const copy2 = items.slice();
// concat()
const copy3 = [].concat(items);
// ์ ๊ฐ ๊ตฌ๋ฌธ
const copy4 = [...items];
์์ ๋ณต์ฌ์ ํน์ง
์์ ๋ณต์ฌ๋ ๊ฐ์ฒด์ ์ต์์ ์์ฑ์ ์ฌํ ๋นํ๋ฉด ์๋ณธ ๊ฐ์ฒด์ ์ํฅ์ ์ฃผ์ง ์์ต๋๋ค. ํ์ง๋ง ์ค์ฒฉ๋ ๊ฐ์ฒด์ ์์ฑ์ ๋ณ๊ฒฝํ๋ฉด ์๋ณธ๋ ํจ๊ป ๋ณ๊ฒฝ๋ฉ๋๋ค.
const ingredientsList = ["๊ตญ์", { list: ["๊ณ๋", "๋ฐ๊ฐ๋ฃจ", "๋ฌผ"] }];
const ingredientsListCopy = Array.from(ingredientsList);
// ์ต์์ ์์ ๋ณ๊ฒฝ
ingredientsListCopy[0] = "์๊ตญ์";
console.log(ingredientsList[0]); // "๊ตญ์" (์ํฅ ์์)
// ์ค์ฒฉ๋ ๊ฐ์ฒด ๋ณ๊ฒฝ
ingredientsListCopy[1].list = ["์๊ฐ๋ฃจ", "๋ฌผ"];
console.log(ingredientsList[1].list); // ["์๊ฐ๋ฃจ", "๋ฌผ"] (์ํฅ ์์!)
๊น์ ๋ณต์ฌ(Deep Copy)๋?
๊น์ ๋ณต์ฌ๋ ๊ฐ์ฒด์ ๋ชจ๋ ์ค์ฒฉ๋ ์์ฑ๊น์ง ์์ ํ ์๋ก์ด ๋ณต์ฌ๋ณธ์ ๋ง๋๋ ๋ฐฉ์์ ๋๋ค. ๋ณต์ฌ๋ณธ์ ์์ ํด๋ ์๋ณธ ๊ฐ์ฒด์ ์ ํ ์ํฅ์ ์ฃผ์ง ์์ต๋๋ค.
๊น์ ๋ณต์ฌ์ ์ ์
๋ ๊ฐ์ฒด๊ฐ ๊น์ ๋ณต์ฌ ๊ด๊ณ์ ์์ผ๋ ค๋ฉด ๋ค์ ์กฐ๊ฑด์ ๋ง์กฑํด์ผ ํฉ๋๋ค:
- ๋ ๊ฐ์ฒด๋ ์๋ก ๋ค๋ฅธ ๊ฐ์ฒด์ฌ์ผ ํฉ๋๋ค (
o1 !== o2) - ์์ฑ์ ์ด๋ฆ๊ณผ ์์๊ฐ ๊ฐ์์ผ ํฉ๋๋ค
- ์์ฑ ๊ฐ๋ค์ด ์๋ก์ ๊น์ ๋ณต์ฌ๋ณธ์ด์ด์ผ ํฉ๋๋ค
- ํ๋กํ ํ์ ์ฒด์ธ์ด ๊ตฌ์กฐ์ ์ผ๋ก ๋์ผํด์ผ ํฉ๋๋ค
๊น์ ๋ณต์ฌ์ ๋์ ์๋ฆฌ
๊น์ ๋ณต์ฌ๋ ์ค์ฒฉ๋ ๋ชจ๋ ๊ฐ์ฒด๋ฅผ ์ฌ๊ท์ ์ผ๋ก ๋ณต์ฌํฉ๋๋ค. ๋ฐ๋ผ์ ๋ณต์ฌ๋ณธ์ ์ด๋ค ๋ถ๋ถ์ ๋ณ๊ฒฝํ๋๋ผ๋ ์๋ณธ์ ์ํฅ์ ์ฃผ์ง ์์ต๋๋ค.
const original = {
name: "์ด์์ง",
projects: {
current: ["ํ๋ก์ ํธ A", "ํ๋ก์ ํธ B"],
completed: ["ํ๋ก์ ํธ X"]
}
};
const deepCopy = JSON.parse(JSON.stringify(original));
deepCopy.projects.current.push("ํ๋ก์ ํธ C");
console.log(original.projects.current); // ["ํ๋ก์ ํธ A", "ํ๋ก์ ํธ B"] (์ํฅ ์์)
๊น์ ๋ณต์ฌ ๊ตฌํ ๋ฐฉ๋ฒ
1. JSON.parse(JSON.stringify()) ํ์ฉ
๊ฐ์ฅ ๊ฐ๋จํ๊ณ ๋๋ฆฌ ์ฌ์ฉ๋๋ ๋ฐฉ๋ฒ์ ๋๋ค.
const ingredientsList = ["๊ตญ์", { list: ["๊ณ๋", "๋ฐ๊ฐ๋ฃจ", "๋ฌผ"] }];
const ingredientsListDeepCopy = JSON.parse(JSON.stringify(ingredientsList));
ingredientsListDeepCopy[1].list = ["์๊ฐ๋ฃจ", "๋ฌผ"];
console.log(ingredientsList[1].list); // ["๊ณ๋", "๋ฐ๊ฐ๋ฃจ", "๋ฌผ"] (์ํฅ ์์)
์ฅ์ :
- ๊ตฌํ์ด ๊ฐ๋จํ๊ณ ์ง๊ด์ ์ ๋๋ค
- ๋ณ๋์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ํ์ ์์ต๋๋ค
๋จ์ :
- ํจ์๋ ๋ณต์ฌ๋์ง ์์ต๋๋ค
Date๊ฐ์ฒด๋ ๋ฌธ์์ด๋ก ๋ณํ๋ฉ๋๋คundefined,Symbol๊ฐ์ ํน์ ๊ฐ์ ๋ฌด์๋ฉ๋๋ค- ์ํ ์ฐธ์กฐ๊ฐ ์์ผ๋ฉด ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค
const obj = {
date: new Date(),
func: () => console.log("hello"),
undef: undefined
};
const copy = JSON.parse(JSON.stringify(obj));
console.log(copy);
// { date: "2024-01-15T10:30:00.000Z" }
// func์ undef๋ ์ฌ๋ผ์ง๋๋ค
2. structuredClone() ์ฌ์ฉ
JavaScript์์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ ๊ณตํ๋ ๋ฉ์๋์ ๋๋ค.
const original = {
name: "์ต์ ์ง",
date: new Date(),
nested: {
data: [1, 2, 3]
}
};
const deepCopy = structuredClone(original);
deepCopy.nested.data.push(4);
console.log(original.nested.data); // [1, 2, 3] (์ํฅ ์์)
console.log(deepCopy.date instanceof Date); // true
์ฅ์ :
Date,RegExp,Map,Set๋ฑ ๋ค์ํ ๋ด์ฅ ๊ฐ์ฒด๋ฅผ ์ ํํ ๋ณต์ฌํฉ๋๋ค- ์ํ ์ฐธ์กฐ๋ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค
- ์ฑ๋ฅ์ด ์ฐ์ํฉ๋๋ค
๋จ์ :
- ํจ์๋ ์ฌ์ ํ ๋ณต์ฌ๋์ง ์์ต๋๋ค
- ์ค๋๋ ๋ธ๋ผ์ฐ์ ์์๋ ์ง์ํ์ง ์์ ์ ์์ต๋๋ค
3. ์ฌ๊ท ํจ์๋ฅผ ์ด์ฉํ ์ปค์คํ ๊ตฌํ
์์ ํ ์ ์ด๊ฐ ํ์ํ ๊ฒฝ์ฐ ์ง์ ๊ตฌํํ ์ ์์ต๋๋ค.
function deepClone(obj, hash = new WeakMap()) {
// null์ด๋ ์์ ํ์
์ ๊ทธ๋๋ก ๋ฐํ
if (obj === null || typeof obj !== 'object') {
return obj;
}
// ์ํ ์ฐธ์กฐ ์ฒ๋ฆฌ
if (hash.has(obj)) {
return hash.get(obj);
}
// Date, RegExp ๋ฑ ํน์ ๊ฐ์ฒด ์ฒ๋ฆฌ
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
// ์ ๊ฐ์ฒด ์์ฑ
const cloneObj = Array.isArray(obj) ? [] : {};
hash.set(obj, cloneObj);
// ์ฌ๊ท์ ์ผ๋ก ๋ณต์ฌ
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
cloneObj[key] = deepClone(obj[key], hash);
}
}
return cloneObj;
}
const original = {
name: "์ ๋ฏผํธ",
skills: ["JavaScript", "React"],
experience: {
years: 5,
companies: ["A์ฌ", "B์ฌ"]
}
};
const copy = deepClone(original);
copy.skills.push("TypeScript");
console.log(original.skills); // ["JavaScript", "React"] (์ํฅ ์์)
์ฅ์ :
- ํ์์ ๋ฐ๋ผ ์ปค์คํฐ๋ง์ด์ง ๊ฐ๋ฅํฉ๋๋ค
- ํน์ํ ํ์ ๋ ์ํ๋ ๋๋ก ์ฒ๋ฆฌํ ์ ์์ต๋๋ค
๋จ์ :
- ๊ตฌํ๊ณผ ์ ์ง๋ณด์๊ฐ ๋ณต์กํฉ๋๋ค
- ์ฃ์ง ์ผ์ด์ค๋ฅผ ๋ชจ๋ ๊ณ ๋ คํด์ผ ํฉ๋๋ค
4. Lodash ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํ์ฉ
lodash๋ ์ค๋ฌด์์ ๋ง์ด ์ฌ์ฉํ๋ ์๋ฐ์คํฌ๋ฆฝํธ ์ ํธ๋ฆฌํฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค.
import _ from 'lodash';
const original = {
name: "๊ฐ์์ฐ",
metadata: {
tags: ["frontend", "react"],
createdAt: new Date()
}
};
const deepCopy = _.cloneDeep(original);
์ฅ์ :
- ๊ฒ์ฆ๋ ๊ตฌํ์ผ๋ก ์์ ์ ์ ๋๋ค.
- ๋๋ถ๋ถ์ ์ฃ์ง ์ผ์ด์ค๋ฅผ ์ฒ๋ฆฌํฉ๋๋ค.
๋จ์ :
- ์ถ๊ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ค์นํด์ผ ํฉ๋๋ค.
- ๋ฒ๋ค ํฌ๊ธฐ๊ฐ ์ฆ๊ฐํ ์ ์์ต๋๋ค. (๋ฌผ๋ก lodash์ ์ฉ๋์ด ํฌ์ง๋ ์์ง๋งโฆ)
์ธ์ ์์ ๋ณต์ฌ๋ฅผ ์ฌ์ฉํ ๊น?
์์ ๋ณต์ฌ๋ ๋ค์๊ณผ ๊ฐ์ ์ํฉ์ ์ ํฉํฉ๋๋ค:
1. ์ฑ๋ฅ์ด ์ค์ํ ๊ฒฝ์ฐ
์ค์ฒฉ๋ ๊ฐ์ฒด๋ฅผ ๋ ๋ฆฝ์ ์ผ๋ก ์์ ํ ํ์๊ฐ ์๋ค๋ฉด, ์์ ๋ณต์ฌ๊ฐ ๋ ๋น ๋ฅด๊ณ ๋ฉ๋ชจ๋ฆฌ ํจ์จ์ ์ ๋๋ค.
const userConfig = {
theme: "dark",
language: "ko"
};
// ์ต์์ ์์ฑ๋ง ๋ณ๊ฒฝํ๋ฏ๋ก ์์ ๋ณต์ฌ๋ก ์ถฉ๋ถ
const newConfig = { ...userConfig, theme: "light" };
2. ์ต์์ ์์ฑ๋ง ๋ณ๊ฒฝํ๋ ๊ฒฝ์ฐ
์ค์ฒฉ๋ ๊ฐ์ฒด๋ฅผ ๊ฑด๋๋ฆฌ์ง ์๋๋ค๋ฉด ์์ ๋ณต์ฌ๋ง์ผ๋ก๋ ์ถฉ๋ถํฉ๋๋ค.
const product = {
id: 1,
name: "๋
ธํธ๋ถ",
price: 1500000
};
const updatedProduct = { ...product, price: 1400000 };
3. React์์ Props ์ ๋ฌ
React์์๋ ๋ถ๋ณ์ฑ์ ์ ์งํ๊ธฐ ์ํด ์์ ๋ณต์ฌ๋ฅผ ์์ฃผ ์ฌ์ฉํฉ๋๋ค.
const handleUpdate = (newData) => {
setUser({ ...user, ...newData });
};
์ธ์ ๊น์ ๋ณต์ฌ๋ฅผ ์ฌ์ฉํ ๊น?
๊น์ ๋ณต์ฌ๋ ๋ค์ ์ํฉ์์ ํ์ํฉ๋๋ค:
1. ์์ ํ ๋ ๋ฆฝ์ ์ธ ๋ณต์ฌ๋ณธ์ด ํ์ํ ๊ฒฝ์ฐ
์๋ณธ๊ณผ ์์ ํ ๋ถ๋ฆฌ๋ ๊ฐ์ฒด๊ฐ ํ์ํ ๋ ๊น์ ๋ณต์ฌ๋ฅผ ์ฌ์ฉํฉ๋๋ค.
const template = {
title: "๊ธฐ๋ณธ ํ
ํ๋ฆฟ",
sections: [
{ type: "header", content: "์ ๋ชฉ" },
{ type: "body", content: "๋ณธ๋ฌธ" }
]
};
// ๊ฐ ์ฌ์ฉ์๋ง๋ค ๋
๋ฆฝ์ ์ธ ํ
ํ๋ฆฟ ํ์
const userTemplate = structuredClone(template);
userTemplate.sections[0].content = "์ฌ์ฉ์ ์ ๋ชฉ";
2. ์ค์ฒฉ๋ ๊ฐ์ฒด๋ฅผ ์์ ํด์ผ ํ๋ ๊ฒฝ์ฐ
๋ณต์กํ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ฅผ ๋ค๋ฃฐ ๋ ๊น์ ๋ณต์ฌ๊ฐ ํ์์ ๋๋ค.
const projectData = {
name: "์ ๊ท ํ๋ก์ ํธ",
team: {
frontend: ["๊น๊ฐ๋ฐ", "์ด๋์์ธ"],
backend: ["๋ฐ์๋ฒ", "์ต๋๋น"]
}
};
const archivedProject = structuredClone(projectData);
archivedProject.team.frontend.push("์ ์
์ฌ์");
// original์ team์ ์ํฅ๋ฐ์ง ์์
3. ์ํ ํ์คํ ๋ฆฌ๋ฅผ ๊ด๋ฆฌํ๋ ๊ฒฝ์ฐ
์คํ ์ทจ์(Undo) ๊ธฐ๋ฅ์ ๊ตฌํํ ๋ ์ ์ฉํฉ๋๋ค.
const history = [];
function saveState(currentState) {
history.push(structuredClone(currentState));
}
function undo() {
return history.pop();
}
์ฑ๋ฅ ๊ณ ๋ ค์ฌํญ
๊น์ ๋ณต์ฌ์ ์์ ๋ณต์ฌ๋ ์ฑ๋ฅ ์ฐจ์ด๊ฐ ์์ต๋๋ค.
์์ ๋ณต์ฌ์ ์ฑ๋ฅ
์์ ๋ณต์ฌ๋ ์ต์์ ์์ฑ๋ง ๋ณต์ฌํ๋ฏ๋ก ๋งค์ฐ ๋น ๋ฆ ๋๋ค. ๊ฐ์ฒด์ ํฌ๊ธฐ๊ฐ ํด์๋ก ๊น์ ๋ณต์ฌ ๋๋น ์ฑ๋ฅ ์ด์ ์ด ํฝ๋๋ค.
// ๋น ๋ฆ
const shallowCopy = { ...largeObject };
๊น์ ๋ณต์ฌ์ ์ฑ๋ฅ
๊น์ ๋ณต์ฌ๋ ๋ชจ๋ ์ค์ฒฉ ๊ตฌ์กฐ๋ฅผ ์ํํ๋ฏ๋ก ๋น์ฐํ ์๊ฐ์ด ๋ ์ค๋ ๊ฑธ๋ฆฝ๋๋ค.
// ์๋์ ์ผ๋ก ๋๋ฆผ
const deepCopy = structuredClone(largeObject);
์ฑ๋ฅ ์ต์ ํ ํ
๋์ฉ๋ ๊ฐ์ฒด๋ฅผ ๋ค๋ฃฐ ๋๋ ๋ค์์ ๊ณ ๋ คํ์ธ์:
- ํ์ํ ๋ถ๋ถ๋ง ๋ณต์ฌํ๊ธฐ
const { metadata, ...essentialData } = largeObject;
const copy = structuredClone(essentialData);
- ๋ฉ๋ชจ์ด์ ์ด์ ํ์ฉ
const cache = new WeakMap();
function getCachedCopy(obj) {
if (!cache.has(obj)) {
cache.set(obj, structuredClone(obj));
}
return cache.get(obj);
}
- ์ ์ ํ ๋ฐฉ๋ฒ ์ ํ
- ๊ฐ๋จํ ๊ฐ์ฒด:
JSON.parse(JSON.stringify()) - ๋ณต์กํ ๊ฐ์ฒด:
structuredClone() - ์ปค์คํ ๋ก์ง ํ์: ์ง์ ๊ตฌํ ๋๋ Lodash
์์ฃผ ๋ฌป๋ ์ง๋ฌธ
Q: ๋ฐฐ์ด๋ ๊ฐ์ ๋ฐฉ์์ผ๋ก ๋ณต์ฌํ๋์?
๋ค, ๋ฐฐ์ด๋ ๊ฐ์ฒด์ด๋ฏ๋ก ๋์ผํ ์๋ฆฌ๊ฐ ์ ์ฉ๋ฉ๋๋ค. ์์ ๋ณต์ฌ๋ ์ ๊ฐ ๊ตฌ๋ฌธ์ด๋ slice()๋ฅผ, ๊น์ ๋ณต์ฌ๋ structuredClone()์ ์ฌ์ฉํ๋ฉด ๋ฉ๋๋ค.
Q: React์์๋ ์ด๋ค ๋ณต์ฌ ๋ฐฉ์์ ์ฃผ๋ก ์ฌ์ฉํ๋์?
React์์๋ ๋๋ถ๋ถ ์์ ๋ณต์ฌ๋ฅผ ์ฌ์ฉํฉ๋๋ค. ์ํ ์
๋ฐ์ดํธ ์ { ...state } ๊ฐ์ ๋ฐฉ์์ผ๋ก ์ ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด ๋ถ๋ณ์ฑ์ ์ ์งํฉ๋๋ค.
Q: JSON ๋ฐฉ์์ ๊น์ ๋ณต์ฌ๋ก ์ถฉ๋ถํ์ง ์๋์?
๊ฐ๋จํ ๋ฐ์ดํฐ ๊ตฌ์กฐ์๋ ์ถฉ๋ถํ์ง๋ง, Date, ํจ์, undefined ๋ฑ์ ๋ค๋ค์ผ ํ๋ค๋ฉด structuredClone()์ด๋ Lodash๋ฅผ ์ฌ์ฉํ๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์
๋๋ค !