Hard Copy와 Shallow Copy, Object 복사에 대하여

2019년 09월 27일
JavaScript에서 개발을 하던 중 신기한 오류에 봉착했다.
 
// 오브젝트 생성 let obj1 = { a: 1, b: 2, }; // obj2에 obj1을 복사 let obj2 = obj1; // obj2.b를 3으로 수정 obj2.b = 3; // obj1를 출력시 b의 값은 3 console.log(obj1) // { a: 1, b: 3 }
정말 아이러니한 상황이 아닐 수 없다.
Object를 새로 복사하여 값을 수정하였음에도 이전 값이 변경이 되었다.
이 문제는 배열 역시 마찬가지였다.
const arr = [1, 2, 3, 4, 5]; const arr2 = arr; arr2.push(6); console.log(arr); // [1, 2, 3, 4, 5, 6]
어째서 이러한 현상이 일어나는 것일까?
간단히 설명하자면 아래 그림을 참고해보자.
단순 대입등을 이용한 Object 복사는 Shallow Copy(이하 옅은 복사)로, Object의 값을 복사하는 것이 아닌 Object 값들의 메모리 주소를 복사한다.
컴퓨터는 데이터를 저장할 때에 메모리에 데이터를 저장한 후 저장된 데이터의 메모리 주소를 이용하여 데이터를 탐색한다.
옅은 복사는 이 메모리 주소를 복사하여 불러오기 때문에 다른 Object에서 값을 변경하더라도 같은 메모리 주소를 참조하기 때문에
원본의 데이터 역시 값이 변경되는 것이다.
메모리 주소가 아닌 값을 복사하는 복사 방법이 있는데 이를 Deep Copy(깊은 복사)라고 한다.
이는, 해당 Object의 값을 새로이 메모리에 저장한 후 해당 데이터의 메모리 주소를 가진다.
그렇다하면 깊은 복사를 사용하기 위해서는 어떻게 해야할까?
 
function copyWithJSON(obj) { return JSON.parse(JSON.stringify(obj)); } const source = { a: 1, b: 2 }; const copyObj = copyWithJSON(source); copyObj.b = 3; console.log(source.b); // 2
첫번째 방법은 Object를 JSON String으로 변경한 후 Object로 재변환하는 방법이다.
이렇게 하면 기존의 메모리 주소를 잃고 새로운 Object가 만들어지기 때문에 이전 메모리를 참조하지 않는다.
 
function copyWithAssign(obj) { return Object.assign({}, obj); } const source = { a: 1, b: 2 }; const copyObj = copyWithAssign(source); copyObj.b = 3; console.log(source.b); //
Object.assign은 두 오브젝트를 병합하여 새로운 오브젝트를 만들어내는 메소드이다.
이 점을 이용하여 assign의 병합할 오브젝트를 {} 과 같이 빈 Object로 지정한다면 그저 새로운 Object가 생성될 뿐이다.

 
+ 2022.04.20. 추가
let obj = { a: 1, b: 2, }; const copy = structuredClone(obj); copy.a = 10; console.log(obj.a); // 1, not changed!
최신 브라우저를 사용한다면 structuredClone() 함수를 사용하면 쉽게 깊은 복사를 할 수 있다.
structuredClone() 은 자바스크립트 메소드 내에서 사용하는 깊은 복사 알고리즘이다.