우리가 자바스크립트에서 객체나 데이터의 값을 복사할 때 복사하는 방법에 따라 깊은 복사와 얕은 복사로 나뉩니다.
원시값(primitive value)은 변경 불가능한 값으로, 값을 만들어내는 기본적인 데이터 타입입니다. 하지만 변경불가능한 것은 변수가 아닌 값에 대한 진술이므로, 재할당은 가능합니다. 따라서 재할당이 불가능한 상수와 변경 불가능한 값인 원시값은 동일한 의미는 아닙니다. 아래는 대표적인 원시값의 기본적인 데이터 타입입니다.
해당 데이터 타입에 변수를 선언하면 원시값이 생성됩니다. 이후 새로운 원시 값을 재할당 할 시 기존 원시값을 변경하는 것이 아닌 새로운 메모리 공간에 값을 저장한 뒤 변수가 참조하는 주소를 변경합니다. 이것을 원시값의 불변성이라고 합니다.
참조(reference) 값은 메모리 상에서 실제 값이 저장되어 있는 위치를 가리키는 값입니다. 참조 값을 변수에 할당하면, 해당 변수에는 값이 아닌 메모리 상의 위치 정보가 저장됩니다. 다른 변수에 할당하면, 그 변수에도 동일한 위치 정보가 저장되어 두 변수는 같은 객체를 참조하게 됩니다. 아래는 대표적인 참조값 객체타입 입니다.
참조 값이 같은 변수는 같은 객체를 참조하며, 한 변수의 값이 변경되면 같은 객체를 참조하는 다른 변수의 값도 함께 변경됩니다.
let yuniAddress = 'seoul';
let mimiAddress = yuniAddress;
mimiAddress = 'busan';
console.log(yuniAddress); // seoul
console.log(mimiAddress); // busan
유니의 주소는 서울이라고 원시값을 설정했습니다. string은 원시값이니까요. 자 그리고 미미도 서울사람이라 유니의 주소와 같이 설정했었으나, 미미가 부산으로 가 다시 부산이라고 재할당 해주었습니다. 이 경우 미미가 서울에 살았다는 사실은 변하지 않을 것입니다. 다만 주소지가 변경되어 현재 미미가 가르키는 주소가 다른거지요. 앞에서 나온 원시값의 불변성 때문이며, 이러한 복사방법을 깊은복사라고 합니다.
const person = {
name: 'yuni'
};
let personCopy = person;
personCopy.name = 'kim';
console.log(person, personCopy); // {name: kim} {name:kim}
person은 객체이므로 참고값이며, 참조 값을 변수에 할당하면 해당 변수에는 값이 아닌 메모리 상의 위치가 저장된다고 앞에서 배웠습니다. 또한 참조 값이 같은 변수는 같은 객체를 참조하며, 한 변수의 값이 변경되면 같은 객체를 참조하는 다른 변수의 값도 함께 변경되기 때문에 person의 name과 personCopy의 name이 모두 kim으로 변한 것 입니다.
왜 참조값은 원시값처럼 복사가 진행되지 않을까요?
정답은 성능과 효율에 있습니다. 깊은복사를 하게 되면 객체 안 코드 한 줄, 한 줄 복사를 진행할 것이고 속도도 두배, 용량도 두배가 들겠죠. 그런데 얕은복사를 하게 되면 person이라는 위치 하나만 공유하면 되기 때문에 용량과 속도 면에서 모두 이득입니다. 이러니 얕은 복사를 진행하는 겁니다.
const person = {
name: 'yuni'
};
let personCopy = {...person};
personCopy.name = 'mimi';
console.log(person, personCopy);
Spread연산자를 이용하면 됩니다. 이렇게 하면 깊은 복사처럼 person을 사본을 만들어 personCopy에 할당하게 됩니다. 이러는 경우 데이터가 2개가 존재하기 때문에 서로 다른 값을 가질 수 있습니다.
하지만 이 spread연산자에도 함정이 있는데 바로 1 depth까지는 확실하게 깊은 복사가 일어나지만 2 depth 이상인 경우는 얕은 복사로 바뀐다는 점입니다. 이 문제의 해결 방법들은 앞으로의 포스팅에서 다뤄 보도록 하겠습니다.