객체의 얕은 복사 vs 깊은 복사

윤성민·2020년 9월 28일
4

최근 react, vue 개발을 하면서 객체의 얕은 복사(깊은 복사인 줄..) 때문에 시간을 많이 허비했다. 그렇기 때문에 블로그를 통해 개념을 다시 정리해보는 시간을 갖게되었다.

기본적으로 자바스크립트에서

const obj = { a: 1, b: 2}
const obj2 = obj
console.log(obj === obj2) // true

객체를 대입하게 되면, 참조에 의한 할당이 이루어지므로(얕은 복사) 둘은 같은 객체이다.
다른 사람에게 설명할 때, 껍데기라는 표현을 쓰는데 이 객체는 같은 껍데기를 참조하기 때문에, 둘은 같은 객체라고 빗대서 표현하는데 좀 어색한 부분도 있긴 하지만 나는 이런식으로 이해하고 있다.

얕은 복사가 이루어졌을 때, 주로 발생하는 문제점은 다음과 같다.

const obj = { a:1, b:2 }
const obj2 = obj
obj2.a = 100
console.log(obj.a) // 100

놀랍게도, obj2 객체를 수정했는데, obj 도 수정된 효과를 볼 수 있었다. 얕은 복사는 참조에 의한 할당이기 때문에, 내부 객체의 property를 공유하게 된다.

그렇다면, 깊은 복사를 하기 위해서는 어떻게 해야할까?

const obj = { a:1, b:2 }
const obj2 = {...obj}
obj2.a = 100
console.log(obj === obj2) // false 
console.log(obj.a) // 1

...(spread) 연산자를 통해 obj의 key-value 쌍을 복사하여 obj2에 할당한다. 결국 새로운 껍데기를 갖게 되었으므로, obj2와 obj는 다른 id를 갖게되고, 이는 깊은 복사라 할 수 있다.
라고, 알고 있었지만 전개 구문, 객체, 깊은 복사에 대해 잘못 이해하였기 때문에, 앞서 말했던 개발도중 대참사가 일어났다.

const obj = { a: { b:1, c:1 }, d: 2}
const obj2 = {...obj}
obj.a.b = 100
console.log(obj === obj2) // false
console.log(obj2.a.b) // 100

분명 앞에서 ...연산자로 복사를 하고, 새로운 껍데기를 부여했기 때문에, 앞서 정의한 깊은 복사가 이루어진게 맞다. 하지만 obj2.a.b의 결과를 봤을 때, 이는 앞에서 본 얕은 복사에서 일어났던 현상과 매우 비슷하다. 이게 어떻게 된 일일까?

사실 ...연산자를 통해 복사한 건, 놀랍게도 얕은 복사이다. 앞서 ...연산자를 활용해 복사를 했을 때, 깊은 복사가 된 것은 depth가 한단계 깊이 였기 때문에 가능한 일이었다.

MDN의 전개 구문을 다시 확인해보니,

참고: Spread 문법은 배열을 복사할 때 1 레벨 깊이에서 효과적으로 동작합니다. 그러므로, 다음 예제와 같이 다차원 배열을 복사하는것에는 적합하지 않을 수 있습니다. (Object.assign() 과 전개 구문이 동일합니다)

var a = [[1], [2], [3]];
var b = [...a];
b.shift().shift(); // 1
// 이제 배열 a 도 영향을 받음: [[], [2], [3]]

라고 작성되어 있었다. 역시 공식문서에는 해답이 있다. 찾지 못했을 뿐...

결국 1레벨 깊이 이상의 객체를 깊은 복사하려면,

const obj = { a: { b:1, c:1 }, d: 2}
const obj2 = {...obj, a:{...obj.a}}
obj.a.b = 100
console.log(obj === obj2) // false
console.log(obj2.a.b) // 1

이런 식으로, 한 단계 더 깊이 들어가서 ...연산자를 다시 활용해주어야 한다.
실제 개발에서 배열안에 객체를 다루는 일이 매우 많기 때문에, 이 점을 주의해서 개발해야 한다.

결론
1. ...연산자는 얕은 복사이다.
2. 객체가 서로 다르다고 깊은 복사가 이루어진건 아니다.
3. 객체를 깊은 복사하려면, 객체의 깊이 끝까지 복사하거나, lodash 라이브러리를 사용하여 deep copy하자!

profile
기록은 기억보다 강하다.

0개의 댓글