어떤 데이터가 복사될 때, 얕은 복사가 될 수도 있고 깊은 복사가 될 수도 있습니다. 이번 포스트에서는 전개 구문을 통한 깊은 복사를 다루려고 합니다. 그전에 얕은 복사는 무엇이고 깊은 복사는 무엇인지부터 간단하게 짚고 넘어가 보겠습니다.
데이터가 복사될 때 메모리에 새로운 '값'으로 복사되는 것을 의미합니다.
let a = 1;
let b = a;
console.log(a); // 1
console.log(b); // 1
b = 2;
console.log(a); // 1
console.log(b); // 2
b에 a를 할당해 줌으로써 a의 값을 b에 복사했습니다.
복사를 했지만 b의 값을 변경하면 b에는 변경된 값이 할당되고 a는 그대로 원래 값을 유지합니다.
이러한 깊은 복사는 복사되는 값이 메모리에 새로운 값으로 복사됩니다.
그렇게 메모리에 새롭게 만들어진 값을 다른 값으로 변경해도 원본 데이터에는 영향을 주지 않습니다.
깊은 복사와 달리 얕은 복사는 데이터가 복사될 때 메모리에 값을 가지고 있는 메모리의 '주소'가 복사되는 것을 의미합니다.
let student1 = {
name: 'Min',
};
let student2 = student1;
console.log(student1); // { name: 'Min' }
console.log(student2); // { name: 'Min' }
student1.name = 'Won';
console.log(student1); // { name: 'Won' }
console.log(student2); // { name: 'Won' }
student2에 student2를 할당해 줌으로써 student1의 값을 b에 복사했습니다. student1의 값을 변경하면 student2의 값 또한 변경됩니다.
이러한 복사는 값이 아닌 값의 주소를 복사해서 일어나는 현상입니다.
student1과 student2는 같은 객체의 주소를 갖고 있으므로 객체의 내용이 어디에서 변경되는지와는 상관없이 변경이 되면 해당 객체의 주소를 가지고 있는 모든 변수(student1과 student2)에는 변경되는 내용이 업데이트됩니다.
JavaScript에서의 데이터(자료형)은 크게 원시 타입(primitive type)과 참조 타입(reference type)으로 나눌 수 있습니다.
- 원시 타입(primitive type) - number, string, boolean, null, undefined, symbol
- 참조 타입(reference type) - object, function,,
원시 타입의 데이터를 변수에 할당하면 해당 변수가 가리키는 메모리에는 '값' 이 저장되어 있습니다. 반면, 참조 타입의 데이터를 변수에 할당하면 해당 변수가 가리키는 메모리에는 값(객체)을 갖고 있는 '메모리의 주소' 가 저장되어 있습니다.
따라서, 원시 타입의 데이터들이 복사될 때에는 복사되는 값이 메모리에 새로운 값으로 복사되므로 '깊은 복사'가 됩니다.
참조 타입의 데이터들이 복사될 때에는 메모리에 직접 값이 저장되는 것이 아닌 값의 주소가 저장되므로 '얕은 복사'가 됩니다.
전개 구문을 이용하면 객체도 쉽게 깊은 복사를 할 수 있습니다.
const student1 = { name: 'Min' };
const student2 = { ...student1 };
console.log(student1); // { name: 'Min' }
console.log(student2); // { name: 'Min' }
console.log(student1 === student2); // false
student1.name = 'Won';
console.log(student1); // { name: 'Won' }
console.log(student2); // { name: 'Min' }
student1의 name을 변경했음에도 불구하고 student2의 name은 변경되지 않았습니다. 또한, student1과 student2가 같은지 확인했을 때 false가 나오는데 이는 서로 다른 객체의 주소를 가지고 있기 때문입니다. 즉, student2는 student1의 값을 새로운 객체로 복사한 것입니다. 이를 통해, 전개 구문을 이용한 복사는 깊은 복사가 되는 것을 확인할 수 있습니다.
하지만, 전개 구문은 객체의 한 단계만 깊은 복사를 합니다.
const student1 = { name: 'Min', grade: { math: 90, science: 80 } };
const student2 = { ...student1 };
student1.name = 'Won';
console.log(student1); // { name: 'Won', grade: { math: 90, science: 80 } }
console.log(student2); // { name: 'Min', grade: { math: 90, science: 80 } }
student1.grade.math = 70;
console.log(student1); // { name: 'Won', grade: { math: 70, science: 80 } }
console.log(student2); // { name: 'Min', grade: { math: 70, science: 80 } }
console.log(student1.grade.math === student2.grade.math); // true
student1의 math를 변경했음에도 불구하고 student2의 math도 같이 변경되었습니다. 이는 student1의 grade는 객체 { math: 90, science: 80 }가 들어있는 메모리의 주소를 갖고 있는 것이고, student2에 복사될 때에도 같은 메모리 주소가 복사 된 것입니다. 따라서 객체 { math: 90, science: 80 }의 내용이 변경되면 해당 객체 메모리의 주소를 가지고 있는 다른 모든 곳에서도 변경사항이 업데이트되는 것입니다.
이번 포스트에서는 전개 구문을 통한 깊은 복사에 대해 다뤄보았습니다. 잘못된 내용이 있다면 댓글로 알려주세요!🤗