본문 바로가기
📌 Front End/└ JavaScript

[JavaScript] 자바스크립트 깊은 복사(Deep Copy)와 얕은 복사(Shallow Copy)

by 쫄리_ 2023. 3. 10.
728x90
반응형

Pass-by-reference(참조에 의한 전달)

객체의 타입은 객체 타입 또는 참조 타입이라한다. 참조 타입이란 객체의 모든 연산이 실제값이 아닌 참조값으로 처리됨을 의미한다. 원시 타입은 값이 한 번 정해지면 변경할 수 없다(immutable). 객체는 프로퍼티를 변경, 추가, 삭제가 가능하므로 변경 가능(mutable)한 값이라 할 수 있다.

let bar = {
  num : 1
}

let foo = bar;

console.log(bar.num, foo.num); // 1, 1
console.log(bar === foo); // true

bar.num = 2;

console.log(bar.num, foo.num); // 2, 2
console.log(bar === foo); // true

변수 foo에 bar 값을 할당했다. 변수 bar와 foo는 같은 주소를 가리킨다. 즉, 변수 bar와 foo는 동일한 객체를 참조하고 있다.

그래서 변수 bar의 프로퍼티를 변경하면 foo, bar는 같은 값으로 바뀌고, 비교를 했을 때 true가 나온다.

객체는 참조 방식으로 전달되기 때문에 얕은 복사가 된다.

Pass-by-value(값에 의한 전달)

원시 타입은 값으로 전달된다. 객체는 참조에 의해 전달되는 것과는 달리 값을 복사하여 전달한다. 같은 주소를 가리키게 하는 것과 달리 아에 새로운 주소의 같은 값으로 복사를 하는 것이다.

또한, 원시 타입은 값이 한번 정해지면 변경할 수 없고, 고정된 메모리(숫자형 = 8byte, 영어 = 1byte, 한글 = 2byte)를 가지고있다.

let bar = 1;
let foo = bar;

console.log(bar, foo); // 1, 1
console.log(bar === foo); // true

foo = 10;

console.log(bar, foo); // 1, 10
console.log(bar === foo); // false

변수 bar는 원시(숫자) 타입 1을 가지고 있다. 이 때, foo에 bar를 할당하면 변수 bar의 값인 1이 복사되어 변수 foo에 저장된다. 깊은 복사가 되었다고 보면 된다.

얕은 복사(Shallow Copy)

얕은 복사는 중첩되어 있는 객체들은 복사하지 않고 가장 상위에 있는 것들만 복사한다. 따라서 중첩된 객체들은 원본 객체들과 같은 주소를 참조한다.

const flower = [{ name: "Lily", color: "white" }];
const clone = [...flower];
clone[0].name = "Camellia";
console.log(flower); // [{name: "Camellia", color: "white"}]
console.log(clone); // [{name: "Camellia", color: "white"}]

같은 주소를 가리키고 있기 때문에, 복제된 clone에서 name을 "Camellia"로 변경하면 원본인 flower의 name도 "Camellia"로 변경된다.

또 다른 예시를 들어보자.

let array = [{ name: 'son' }, { name: 'kwak' }, { name: 'lee' }];
let shallow = Array.prototype.slice.call(array);
console.log(shallow); //[{ name: 'son' }, { name: 'kwak' }, { name: 'lee' }]
shallow[0].name = 'jung';
shallow[1] = 'e';
console.log(array); // [{ name: 'jung' }, { name: 'kwak' }, { name: 'lee' }]
console.log(shallow) // [{ name: 'jung' }, e, { name: 'lee' }]

위의 예제에서는 shallow[0].name은 array에 적용되지만, shallow[1]은 적용되지 않는다. 가장 상위 객체(배열 shallow)에 직접 변경하는 것은 적용되지 않지만 내부 객체들은 참조로 이어져있기 때문에 적용된다. shallow[1]이 실행되는 순간 참조하는 주소가 달라지기 때문에 다른 값을 가지게 된다.

그림으로 조금 더 직관적으로 알아보자.

얕은 복사로 복사가 된 객체들은 같은 주소를 참조하고 있기 때문에 원본이나, 복사본의 값을 변경하면 두 값이 모두 바뀐다. 잎을 핑크색으로 바꾸게 되면 두 꽃 모두 분홍색으로 바뀌게 된다.

깊은 복사(Deep Copy)

가장 상위에 있는 객체 뿐만 아니라 중첩된 객체들까지 모두 복사한다. 새로운 주소를 가지게 된다. 원본과 다른 아에 새로운 객체가 생성된다.

const flower = { name: "Lily", color: "white" };
const clone = { ...flower };
clone.name = "Camellia";
console.log(flower); // {name: "Lily", color: "white"}
console.log(clone); // {name: "Camellia", color: "white"}

서로 다른 값을 가리키고 있기 때문에, 복제된 clone에서 name을 "Camellia"로 변경해도 원본인 flower의 name은 "Lily"로 변경되지 않았다.

깊은 복사로 복사가 된 객체들은 서로 다른 주소를 참조하고 있기 때문에 원본이나, 복사본의 값을 변경하면 두 값이 따로 변하게 된다. 잎을 핑크색으로 바꾸게 되면 바뀐 화분의 꽃만 바뀌게 된다.

Reference

728x90
반응형