[JavaScript] 얕은 복사와 깊은 복사

J·2023년 6월 14일
0

JavaScript

목록 보기
8/11

원시 값과 참조 값을 복습하며 오늘은 얕은 복사와 깊은 복사를 좀 더 알아보자. 이들은 프로그래밍에서 참조 값을 처리할 때 사용되는 개념이며 객체를 복사할 때 사용함.

얕은 복사(shallow copy)란?

  • 원본 데이터와 복사한 데이터가 동일한 참조를 공유함. 즉, 메모리 주소를 공유하는 복사. 이로 인해 원본 데이터와 복사한 데이터의 변경이 서로에게 영향을 줌. 참조 값의 복사!
  • 데이터가 새로 생성되는 것이 아니라 원본 데이터의 참조 값을 전달하여 한 데이터를 공유하는 것.
  • 한 단계까지만 복사하는 것.

얕은 복사 알아보기

const obj1 = { a: 1, 
	b: { 
		c: 2 
	} 
};

// Object.assign()
const obj2 = Object.assign({}, obj1);
// Spread Operator
const obj3 = { ...obj1 };

console.log(obj1 === obj2); // Output: false
console.log(obj1 === obj3); // Output: false
// 얇은 복사로 객체 복사 -> 원본 !== 복사본

console.log(obj1.a === obj2.a); // Output: true
console.log(obj1.b === obj3.b); // Output: true
// 1depth 까지 복사 되기 때문에 2depth 이상을 비교해보면 원본과 복사본의 값이 같다고 나옴.

obj1.a = 100;
console.log(obj1.a); // Output: 100 (변경 됨)
console.log(obj2.a); // Output: 1 (변경 없음)
console.log(obj3.a); // Output: 1 (변경 없음)
// 원본 객체(obj1)의 가장 상위 속성 a의 값을 변경함.
// 얕은 복사는 최상위 속성의 값들이 복사되어 저장됨.
// 변경된 원본 객체(obj1)와 복사본 객체(obj2, obj3) 간에 상위 속성 a의 값이 독립적으로 존재. 따라서 원본 객체의 속성 a 값 변경은 복사본 객체에 영향을 주지 않음.

obj2.b.c = 200;
console.log(obj1.b.c); // Output: 200 (변경 됨)
console.log(obj2.b.c); // Output: 200 (변경 됨)
console.log(obj3.b.c); // Output: 200 (변경 됨)
// 복사본 객체(obj2)의 중첩 객체의 속성(b.c) 값을 변경.
// 얕은 복사는 중첩 객체의 참조값을 공유함.
// 따라서 원본 객체(obj1)와 복사본 객체(obj2)의 중첩 객체 속성 b는 동일한 객체를 참조, 복사본 객체의 속성 b.c 값 변경은 원본 객체와 다른 복사본 객체(obj3)에도 영향을 미침.

// 배열은 slice, concat 등을 사용해 복사할 수 있음.
const arr1 = [1, 
	2, { 
		a: 3 
	}
];
const arr2 = arr1.slice();
const arr3 = arr1.concat();

arr1[0] = 100;
console.log(arr1[0]); // Output: 100 (변경 됨)
console.log(arr2[0]); // Output: 1 (변경 없음)
console.log(arr3[0]); // Output: 1 (변경 없음)

arr2[2].a = 300;
console.log(arr1[2].a); // Output: 300 (변경 됨)
console.log(arr2[2].a); // Output: 300 (변경 됨)
console.log(arr3[2].a); // Output: 300 (변경 됨)

얕은 복사가 적합한 경우

  • 데이터 구조의 변경 사항이 복사본에 반영되어야 하는 경우.
  • 원본 데이터 구조의 크기가 크거나 복사 과정에서 성능 저하가 우려되는 경우.

깊은 복사(deep copy)

  • 원본 데이터와 동일한 구조와 값을 가지는 완전히 새로운 데이터 생성. 새로운 메모리 주소에 별도의 데이터가 생성되어 원본 객체와 복사한 객체가 서로 독립적임. 데이터 변경 시 서로에게 영향을 주지 않음. 즉, 실제 값 자체가 복사되는 것!
  • 이로 인한 단점은 메모리를 더 많이 차지하게 될 것임.
  • 객체에 중첩되어있는 객체까지 모두 복사하는 것.

깊은 복사 알아보기

const originalObj = {
  a: 1,
  b: {
    c: 2,
  },
};
const deepCopiedObj = JSON.parse(JSON.stringify(originalObj));
// JSON.stringify()를 사용 원본 객체를 JSON 문자열로 변환하고 JSON.parse()를 사용 JSON 문자열을 다시 객체로 변환하여 깊은 복사를 수행.
// JSON.parse()와 JSON.stringify()를 사용한 깊은 복사의 경우 원본 객체에 함수, undefined, Symbol, 순환 참조 등이 포함된 경우 사용할 수 없음. 이런 경우에는 재귀 함수 또는 외부 라이브러리를 사용할 것.

console.log(deepCopiedObj); // Output: { a: 1, b: { c: 2 } }

// 재귀 함수를 사용한 깊은 복사
function deepCopy(obj) {
  if (typeof obj !== 'object' || obj === null) {
    return obj;
  }

  const copy = Array.isArray(obj) ? [] : {};

  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      copy[key] = deepCopy(obj[key]);
    }
  }

  return copy;
}

const copiedObject = deepCopy(originalObj);

console.log(copiedObject); // Output: { a: 1, b: { c: 2 } }

// 이 밖에 Lodash 라이브러리의 cloneDeep() 메서드를 이용해 깊은 복사를 할 수 있음.
import _ from 'lodash';

const deepCopiedObject = _.cloneDeep(deepCopiedObject);

깊은 복사가 적합한 경우

  • 원본 데이터의 변경이 복사본에 영향을 주면 안될 때
  • 데이터 구조가 중요한 역할을 하는 함수나 클래스 등을 복사할 때.

정리

  • 성능과 메모리 사용성은 얕은 복사의 장점이지만 독립적인 복사본을 얻어야 하는 경우 깊은 복사를 사용해야 함.

reference

profile
벨로그로 이사 중

0개의 댓글