[ Javascript ] 변수 및 객체가 복사 및 할당되는 원리 & 얕은 복사와 깊은 복사

2024. 7. 25. 02:08프로그래밍/JavaScript

개요

Javascript에서 변수 및 객체가 내부적으로 어떤 동작 원리에 의해 복사되고 할당되는지 알아본다.

 

Javascript에는 C처러 포인터의 개념이 없기 때문에 메모리에 변수의 값을 할당하는 부분을 신경써주지 않아도 된다. 

그렇다면 변수나 객체를 복사했을 때는 어떻게 될까?

변수의 경우에는 결론적으로 말하자면 서로 같은 원시타입(primitive type)이 값을 갖고 있다 하더라도 서로 해당 값을 가르키는 메모리의 주소가 다르다. 변수를 2개 선언했다면 식별자도 다를 것이다.

또한 Javascript에서는 객체를 변경가능하게끔 설계했다. 만약 위에서 설명한 변수가 복사되는 원리로 객체를 복사하게 되면 오버헤드가 커지고 성능이 저하 되게 될 것이다. 객체는 훨씬 더 많은 메모리를 사용할 것이기 때문이다. 

따라서 하나의 객체에서 프로퍼티를 추가 제거, 값을 변경하는 등의 작업을 하나의 메모리 주소에서 모두 수행한다.

이와 관련된 개념을 얕은 복사, 깊은 복사 라고 한다.

이로 인해 발생하는 문제점을 알아보자

 

문제점

만약 객체를 복사하게 된다면 개발자의 의도대로 동작하지 않을 수도 있다.

const obj1 = {
    x:10,
    y:20
}
const obj2 = obj1; # 객체 obj1의 메모리 주소를 obj2가 참조
obj2.x = 30;       # obj1과 obj2가 바라보고 있는 메모리 주소의 객체 프로퍼티 x의 값을 변경
console.log(obj1.x)# obj객체의 프로퍼티 값이 변경됨

 

위 결과를 보게 되면 당연한 결과가 나온다. 일반적으로 다른 프로그래밍 언어에서도 객체를 복사해서 사용하게 되면 실질적으로는 객체의 주소를 참조하게 되고 이는 곧 같은 메모리 주소를 가르키는 것을 뜻하기 때문이다.

 

얕은 복사

 

얕은 복사 - MDN Web Docs 용어 사전: 웹 용어 정의 | MDN

객체의 얕은 복사는 복사본의 속성이 복사본이 만들어진 원본 객체와 같은 참조 (메모리 내의 같은 값을 가리킴)를 공유하는 복사입니다. 따라서 원본이나 복사본을 변경하면, 다른 객체 또한

developer.mozilla.org

 

const obj1 = {
    x:{
        z: 50
    },
    y:20
}
const obj2 = { ...obj1 };
console.log(obj1 === obj2);     # false
console.log(obj1.x === obj2.x); # true

 

위 와 같이 객체를 복사하게 되면 새로운 객체를 생성한 것이므로 객체를 값으로 갖는 식별자의 메모리 주소는 서로 다른 주소를 가르키게 되므로 false가 출력된다. 그러나 객체안의 객체가 있는 경우에는 완벽하게 복사된 것이 아니라 true가 된다. 즉 key가 x인 객체는 obj1과 obj2가 서로 같은 객체를 가르키고 있다는 뜻이 된다. 이를 얕은 복사라고 한다. 여기서 말하는 복사란 값은 같으면서 서로 다른 주소값을 가져서 reference by value가 일어나지 않게 복사가 됐다는 뜻이다.
참고로 빌트인 메서드인 Object.create이나 Object.assign 등은 얕은 복사를 수행한다.

 

배열의 경우 객체와는 조금 다르다.

const ingredientsList = ["noodles", { list: ["eggs", "flour", "water"] }];

const ingredientsListCopy = Array.from(ingredientsList);
console.log(ingredientsListCopy);
// ["noodles",{"list":["eggs","flour","water"]}]
ingredientList[0] = 'test';
console.log(ingredientsListCopy[0]);

 

Copy 배열은 test로 변경한 값이 출력되지 않는다.


그럼 다음 동작을 예측해보자.

const obj1 = {
    x:{
        z: 50
    },
    y:20
}
const obj2 = { ...obj1 };
console.log(obj1 === obj2);
obj1.a = 30;
console.log(obj1)
console.log(obj2)
console.log(obj1.x === obj2.x);
obj1.x.z = 100;
console.log(obj1)
console.log(obj2)

 

객체인 obj1과 obj2는 다른 객체이므로 obj1.a라는 프로퍼티를 추가하더라도 obj2에는 새로운 프로퍼티가 할당되지 않는다.

그러나 obj1.x와 obj2.x는 서로 같은 값이다. 즉 같은 객체를 가르키고 있으므로 해당 객체 내부의 프로퍼티를 수정하면 같이 변경된다.

따라서 obj1과 obj2의 x객체 내부의 z프로퍼티의 값은 둘 다 100으로 변경되게 된다. 

깊은 복사는 라이브러리를 사용해 구현할 수 있으므로 참고.

 

깊은 복사

 

깊은 복사 - MDN Web Docs 용어 사전: 웹 용어 정의 | MDN

객체의 깊은 복사는 복사본의 속성이 복사본이 만들어진 원본 객체와 같은 참조(메모리 내의 같은 값을 가리킴)를 공유하지 않는 복사입니다. 따라서 원본이나 복사본을 변경할 때, 다른 객체가

developer.mozilla.org

 

객체 전체가 서로 다른 주소값을 가지게끔 완벽하게 본사된 것을 뜻하며 이를 위해서 MDN에서는 직렬화 역직렬화를 가지는 방법을 설명해주고 있다. 깊은 복사를 수행하게 되면 서로 참조하는 값 없이 완벽하게 복사가 된다.

 

 

 

출처 :

https://developer.mozilla.org/ko/docs/Glossary/Shallow_copy https://developer.mozilla.org/ko/docs/Glossary/Deep_copy