자바스크립트

[JavaScript] Spread Operator(전개 연산자)로 객체를 복사한다면?

[FE] Lighthouse 2024. 11. 21. 15:38

Overview

Spread Operator는 ES6에 나온 연산자 입니다. 객체에 있는 프로퍼티나 배열 내 값을 전개하는데 사용합니다. 스프레드 연산자를 사용하면 객체나 배열을 복사할 수 있습니다. 이때 발생하는 복사는 깊은 복사, 얕은 복사 중 어떤 복사일까요?
가볍게 생각하자면 얕은 복사는 단순히 메모리의 참조 주소 값을 전달하는 것이고, 깊은 복사는 메모리에 같은 모양의 새로운 객체를 생성하는 것을 의미합니다.

결론부터 말하자면 스프레드 연산자를 통해 객체, 배열을 복사하는 행위는 얕은 복사입니다.
1-depth의 프로퍼티 까지는 깊은 복사로 복사가 됩니다. 하지만 2-depth 부터의 프로퍼티 값은 원본 객체의 참조 값을 유지합니다.

 

스프레드 연산자를 활용한 객체 복사의 동작 방식

  • 1-depth의 프로퍼티를 새로운 객체에 복사합니다. 즉, 객체의 첫 번째 계층(직접 속성)만 복사되어 새로운 객체가 생성됩니다.
  • 중첩된 객체나 배열 등 참조형 데이터는 기존 객체의 참조를 유지합니다. 따라서 하위 계층의 데이터는 원본과 동일한 메모리 주소를 참조하게 됩니다.

 

코드로 이해해보기

const b = {
  value: 1,
  c: {},
};

const obj = {
  value: 1,
  b: b,
};

const obj2 = { ...obj };
console.log(obj, obj2);

obj2.value = 3;
obj2.b.value = 3;

console.log(obj, obj2);

 

 
obj2 객체는 obj2 객체를 spread해서 만들었습니다.
그래서 처음 console을 찍으면 첫 번째 값이 나오는데, 그 다음 obj2객체의 value를 3으로 변경하고, obj2.b 객체의 value도 3으로 변경했습니다.

그 결과 값을 찍어보면, obj2.value는 깊은 복사를 통해 메모리 상 새로운 공간에 만들어진 객체의 프로퍼티이기 때문에 obj.value에 영향을 미치지 않지만, obj2.b 객체는 참조를 그대로 유지하기 때문에 obj2.b.value를 변경하면 obj.b.value 의 값도 3으로 변경된 것을 확인할 수 있습니다.
 
이를 메모리 구조로 살펴보면 아래와 같습니다.

메모리 구조

 
obj 변수는 객체가 저장된 주소인 x00020를 저장하고 있습니다. x00020에는 { value: 1, b: x00028 } 값이 저장되어 있습니다. x00028 주소에는 { c: x00032 } 값이 저장되어 있고 x00032 주소에는 {} 값이 저장되어 있습니다.
 
obj2 객체를 스프레드 연산자를 통해 값을 할당합니다. 1depth 프로퍼티 까지는 깊은 복사를 통해 메모리 공간을 새로 할당받고 그 공간에 객체가 저장됩니다. 이 위치가 x00024이고 obj2 변수가 참조하고 있는 x0008 메모리에는 x00024 주소 값이 할당되어 있습니다.
 
x00024 주소에 할당된 객체를 살펴보면 b 프로퍼티에 x00028 주소 값이 할당 되어있는 것을 확인할 수 있고 이 값은 원본 객체가 참조하고 있는 b 주소 값과 일치합니다. 
스프레드 연산자를 통해서 객체를 생성한다면 메모리에는 위와 같은 일이 발생하는 것을 확인할 수 있습니다.
 

아주 깊은 복사를 하고싶다면?

간단한 방법으로는 JSON.parse(JSON.stringify(객체)) 를 사용할 수 있습니다.
두 번째 방법으로는 lodash 라이브러리를 사용한다면 cloneDeep 메서드를 사용해 깊은 복사를 할 수 있습니다.
마지막으로 직접 반복문을 통해 객체를 탐색해 새로운 객체를 만드는 것도 방법입니다.