관리 메뉴

꿈꾸는 개발자

얕은 복사(shallow copy) vs 깊은 복사(deep copy)-Javascript 본문

프로그래밍언어/Javascript

얕은 복사(shallow copy) vs 깊은 복사(deep copy)-Javascript

rickysin 2023. 1. 4. 16:44

공통: 얕은 복사든 깊은 복사든 생성된 객체는 기존과 다른 별개의 객체이다! 

복사에 대한 정의: 여기에서 복사란, 원시값처럼 새로운 값을 복사한다는 의미로 복사되지 않았다는 것은 곧 참조값이 넘어갔다는 의미로, 상호간의 영향을 줌을 의미한다.


얕은 복사(Shallow Copy): 객체에 중첩되어 있는 객체의 경우 참조값을 복사한다     

  • 객체 트리의 노드의 최말단을 복사하지 않는 방식 (따라서, 중첩 객체의 참조값을 복사한다고 한다!)
const original = {
  num: 3,
  age: 24,
  arr: ["a", "b", "c"],
};

const clone1 = { ...original }; //참고 spread문법은 iterable한 객체만 사용할 수 있다

original.num = 4;
console.log(clone1.num); // 3이 나온다

original.arr.push("d");
console.log(original.arr === clone1.arr); //true가 나온다!
  • 언뜻 보면, num의 예시만 보면, 상호간의 영향을 주지 않는 것으로 이해할 수 있지만, arr 즉 배열을 다룰 때는 참조값(reference)을 공유하고 있음을 확인할 수 있다! 
  • Ojbect.assign() 또한 마찬가지로 shallow copy에 머물고 있다. 
    • “For deep cloning, we need to use alternatives because Object.assign() copies property values. If the source value is a reference to an object, it only copies that reference value.” (출처 MDN)
  • JS Method: ex) slice(), concat(), Array.from() 등
    •  원본 배열을 변경하지 않는다! 
    • 얕은 복사이기에 중첩 객체가 있을 경우 복사하지 않고 반환 즉 참조값을 반환하기 때문에 상호간 영향 받음을 의미한다.
let arr1 = [{ a: 1 }, { b: 2 }, { c: 3 }];
let arr2 = arr1.slice();
arr1[0].a = 10;
arr2[1] = "addd";
console.log(arr1, arr2);
  • 위와 같은 예시를 돌려서 코드를 확인해보면

  • 배열 내의 객체의 경우 얕은 복사의 경우 복사되지 않기 때문에 참조값을 공유한다. 따라서, arr1[0].a 의 객체값을 변경하면, arr2의 객체값도 영향을 받음을 확인할 수 있다. 

깊은 복사(Deep Copy): 객체에 중첩되어 있는 객체까지 모두 복사해서 원시 값처럼 완전한 복사본을 만든다. 

  • 객체 트리의 최말단 노드까지 복제하는 방식
const original = {
	  num: 1000,
	  bool: true,
	  arr1: [1, 2, 3, 4, 5],
	  obj1: {
	    x: 5,
	    y: 10
	  },
	  arr2: [
	    [11, 22],
	    [33, 44],
	    [55, 66]
	  ],
	  obj2: {
	    obj3: {
	      1: 1,
	      2: 2
	    },
		    obj4: {
		      3: 3,
		      4: 4,
			      obj5: {
			        mushroom: "🍄"
			      }
		    }
	  },
	  func: function () {
	    console.log("func");
	  }
};
const copy = JSON.parse(JSON.stringify(original));

original.bool = false;
console.log(copy.bool); // true 안바뀜

original.obj2.obj4.obj5.mushroom = "💎";
console.log(copy.obj2.obj4.obj5.mushroom); // 🍄 안바뀜
  • 위의 코드에서는 JSON stringify()와 parse()을 연속적으로 호출했을 때 상호간의 영향을 주지 않음을 확인할 수 있다! 즉 얕은 복사와 달리 중첩 객체까지 함께 복사되는 것이다. 즉, 동일한 객체 트리를 가지는 새로운 객체를 복제하는 것! 
  • 하지만,  JSON에서 함수는 적용되지 않기 때문에 함수가 누락된다는 단점이 존재한다. 
  • 그 외에도 직접 구현이 있지만, 재귀적으로 말단 노드까지 모두 복사해주는 함수를 구현해줘야 하기 때문에 이 마저도 쉽지는 않다! 

Deep Copy 쉽지 않은 것은 알겠다 그래서 해결책은....?

  • 기본적으로 자바스크립트에서 깊은 복사를 수행하기란 쉽지 않다. 내부적으로 그렇게 모델링돼 있지 않기 때문이라 한다. 따라서, 다소 낮은 퍼포먼스를 보이더라도 라이브러리(Lodash 등 )를 이용해서 깊은 복사를 수행해주는 것도 나쁜 방법은 아니다. 하지만, 아키텍처의 관점에서 과연 필요한지를 고민하는 것 또한 개발자의 판단 역량이라고 말하고 있다. 

 

 

참고자료:

- https://www.daleseo.com/js-objects-clone/

 

자바스크립트 객체 복제 방법

Engineering Blog by Dale Seo

www.daleseo.com

https://medium.com/watcha/%EA%B9%8A%EC%9D%80-%EB%B3%B5%EC%82%AC%EC%99%80-%EC%96%95%EC%9D%80-%EB%B3%B5%EC%82%AC%EC%97%90-%EB%8C%80%ED%95%9C-%EC%8B%AC%EB%8F%84%EC%9E%88%EB%8A%94-%EC%9D%B4%EC%95%BC%EA%B8%B0-2f7d797e008a

 

깊은 복사와 얕은 복사에 대한 심도있는 이야기

자바스크립트 개발자라면 반드시 알고 있어야하는 깊은 복사와 얕은 복사에 대해

medium.com

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array#%EB%B0%B0%EC%97%B4_%EB%B3%B5%EC%82%AC%ED%95%98%EA%B8%B0

 

Array - JavaScript | MDN

JavaScript Array 클래스는 리스트 형태의 고수준 객체인 배열을 생성할 때 사용하는 전역 객체입니다.

developer.mozilla.org