TIL

24.09.06. 얕은 복사 VS 깊은 복사(Javascript)

joo1e 2024. 9. 6. 14:48

요약

- 얕은 복사는 참조형 타입의 값(주소)이 바로 아래 단계의 값만 복사

- 깊은 복사원본 객체와 같은 참조(메모리 내의 같은 값을 가리킴)를 공유하지 않는 복사

 

데이터 타입

JavaScript의 얕은 복사와 깊은 복사를 알아보기전에 데이터 타입에 대해서 알아보자. 크게 2종류로 분류할 수 있다.
- 기본형(Primitive)
Number, String, Boolean, undefined, null, 그리고 ES6에서 추가된 Symbol이 있다.
- 타입참조형(Reference)
Object, Array, Function, Date, RegExp, 그리고 ES6에서 추가된 Map, WeakMap, Set, WeakSet이 있다.

기본형과 참조형을 구분할 수 있는 방법은, 할당이나 연산 시 값을 복사하면 기본형이고, 값을 복사할 때 참조하면 참조형이다. 

 

얕은 복사

 참조형 타입의 값의 바로 아래 단계의 값만 복사하는 방법.

var obj1 = {
  a: 1,
  b: {
    c: 2,
  },
};
var obj2 = { ...obj1 };
console.log(obj1 === obj2); // false
console.log(obj1.b === obj2.b); // true

 

두 개의 객체는 다른 주소를 가지고 있지만, 객체 안 프로퍼티는 동일한 주소를 가지고 있다. 쉽게 말하면 {}는 새로 생성된 객체이며 새로운 주소를 갖게 되었고 spread 연산자로 풀어진 프로퍼티들은 처음 선언된 obj1의 프로퍼티들이 사용되었다.

얕은 복사는 후술할 React에서 중요한 개념으로 다루어진다. 얕은 복사를 만드는 방법은 위에 사용한 spread 연산자를 통해 만들 수도 있고 다른 방법으로도 만들 수 있다.

Object.assign()

assign은 ‘할당’이라는 뜻을 가지고 있으며 객체와 객체를 합쳐주는 메서드다.

var obj1 = {
  a: 10,
  b: {
    c: 'abc',
  },
};
var obj2 = Object.assign({}, obj1);
obj2.a = 20;
obj2.b.c = 'def';

console.log(obj1); // { a: 10, b: { c: "def" } }
console.log(obj2); // { a: 20, b: { c: "def" } }
  • 첫번째 인자로 {} 빈 객체가 들어갔기 때문에 껍데기가 obj1과 다른 객체가 반환 될 것이다.
  • obj1의 내용을 {} 빈 객체 안에 복사해서 집어넣고 반환한다.
  • 프로퍼티 a는 기본형 타입의 값이 들어있기 때문에 변경하면 obj2의 a 프로퍼티만 변경된다.
  • 프로퍼티 b는 참조형 타입의 값이 들어있기 때문에 변경하면 obj1, obj2의 b가 가진 주소값이 동일하기 때문에 둘 다 변경된다.

 

깊은 복사

기존 값의 모든 참조가 끊어지는 것. 특히 복사할 때, 참조형 타입 값(객체)에서 내부에 있는 모든 값이 새로운 값이 되는 것.

 

기본형 타입의 깊은 복사

자바스크립트에서는 할당 연산자(=)를 사용해 쉽게 복사를 할 수 있다.

var a = 10;
var b = a;
console.log(a); // 10
console.log(b); // 10

b에 10을 직접 할당해주지 않아도 a를 할당 했기 때문에 10이라는 값이 도출된다. 하지만, 정확하게 말하자면 할당 연산자(=)는 객체를 복사해서 값에 집어 넣는 것이 아니다. 단지 a가 가지고 있는 주소를 b에게 주어 b도 그 대상을 바라보게 만든 것이다. 기본형 타입은 ‘불변값’ 이라고 했다. 기본형 타입의 값을 재할당 할 때는 기존 값을 변경하는 것이 아닌 새로 만들어 주소 값을 준다는 의미였다. 따라서, a와 b는 메모리 공간에 생성된 10의 주소값을 가지고 있다.

var a = 10;
var b = 10;
console.log(a === b); // true
var c = 20;
var d = c;
console.log(c === d); // true

d = 30;
console.log(c); // 20
console.log(d); // 30
console.log(c === d); // false
  • 기본형 타입의 값을 바라보는 주소값이 동일하기 때문에 a와 b는 각각 값을 할당 받았지만 동일하다고 판단한다.
  • d도 마찬가지로 c의 주소를 넘겨받았기 때문에 동일하다고 판단한다.
  • c의 주소를 넘겨 받은 d에 30을 재할당 했다.
  • c와 d는 다른 값을 바라보고 있다.

내부의 값은 없지만 복사했을 때, 서로의 주소가 달라졌다. 기본형 타입의 깊은 복사다. 서로에게 영향을 주지 않는다. 그럼, 참조형 타입을 보자.

 

참조형 타입의 깊은 복사

var obj1 = {
  a: 10,
  b: 'abc',
};
var obj2 = obj1;
console.log(obj1 === obj2); // true

obj2.a = 20;
console.log(obj1); // {a: 20, b: 'abc'}
console.log(obj2); // {a: 20, b: 'abc'}
console.log(obj1 === obj2); // true
  • obj1과 obj2가 가지고 있는 주소 값이 동일하기 때문에 true가 나왔다.
  • obj2의 a프로퍼티의 값을 20으로 재할당했다.
  • obj1, obj2의 값의 a 프로퍼티 모두 20으로 변경되었다.
  • 여전히 obj1과 obj2가 가지고 있는 주소 값이 동일하다.

결과가 나온 이유는, obj2의 ’프로퍼티‘를 변경시켰기 때문이다. 프로퍼티 a가 바라보고 있는 주소 자체는 변경되었지만 obj1, obj2가 프로퍼티 ‘그룹’을 바라보는 주소자체는 변경되지 않은 것이다. 이렇게 객체 자체의 참조 값을 할당하면, 깊은 복사가 일어나지 않는다.

 

 

얕은 복사는 한 단계까지만 복사하고, 깊은 복사는 객체에 중첩된 객체까지 모두 복사한다. 얕은 복사와 깊은 복사 모두 복사한 대상에 대해서 새로운 객체를 생성하여 기존 객체에는 영향을 주지 않는다. 하지만 얕은 복사와 깊은 복사는 어느 수준까지 복사하느냐의 차이를 가진다. 얕은 복사를 하면 한 단계만 복사하기 때문에 중첩된 객체에 대해서는 서로 영향을 주고, 깊은 복사는 중첩된 객체 역시 별개의 값으로서 서로 영향을 주지 않는다.

 

참조 - https://pozafly.github.io/javascript/shallo-copy-and-deep-copy/

'TIL' 카테고리의 다른 글

24.08.15. 마피아 1차 베타 테스트  (0) 2024.08.16
24.08.14.  (0) 2024.08.14
24.08.13.  (0) 2024.08.13
24.08.10. 렌더링  (0) 2024.08.10
24.08.09. 생명주기  (2) 2024.08.09