본문 바로가기
배움 기록/JavaScript

[JS] 얕은 복사(Shallow copy)와 깊은 복사(Deep Copy) 정리

by dygreen 2023. 9. 7.

📌 참조 타입 데이터 (array / object) 특징

자바스크립트의 참조 타입의 데이터는 자료를 하나 만들면, 그 자료를 RAM(= 메모리) 이라는 공간에 저장한다.

 

let arr = [1,2,3];

 

[1,2,3] 자료는 RAM에 저장되고, arr 변수엔 그 자료가 어디있는지 가리키는 화살표만 담겨있다.

* 여기서 화살표란 '값이 저장된 RAM의 주소 값' 를 의미한다.

 

 

📌 얕은 복사 (Shallow Copy)

참조 타입의 얕은 복사는 화살표(= RAM의 주소 값)만 복사하는 것을 말한다.

let arr = [1,2,3];
let arr2 = arr;
arr2[0]++;

console.log(arr2 == arr); // true

 

arr2 라는 다른 변수에 복사를 해도, 화살표만 복사될 뿐이라 arr2 == arrtrue 가 출력된다.

 

 

📌 깊은 복사 (Deep Copy)

참조 타입의 깊은 복사는 (화살표가 다른) 완전한 별개의 array 자료를 만들어서 복사하는 것을 말한다.

 

그러나 깊은 복사는 다차원 데이터를 복사할 경우에 함정이 있다.

즉, '2 depth 이상의 참조 타입 데이터'를 복사했을 때

1 depth 는 깊은 복사가 되지만, 2 depth 이상은 얕은 복사가 된다.

 

위의 경우는 어떤 것이 있고, 이를 해결할 수 있는 방법을 차례대로 알아보자.

 

 


 

 

다차원 데이터 복사 X

📍 Object.assign()

const obj = {
  a: 1,
  b: { c: 2 },
}

const copy = Object.assign({}, obj);

copy.b.c = 3;

console.log(obj); // { a: 1, b: { c: 3 } }
console.log(obj.b.c === copy.b.c); // true
  • 2 depth 의 obj 라는 객체를 copy 에 복사한다.
  • 2 depth 의 값인 copy.b.c 의 값을 변경한다.
  • obj.b.c 의 값과 copy.b.c 의 값을 비교하면 true 로 같다.
  • → 복사된 하위 객체 {c:2} 도 객체이기 때문에 얕은 복사가 된 것이다.
  • → Object.assign() 메서드는 1 depth 객체만 깊은 복사가 되고, 2 depth 이상부터는 얕은 복사가 된다.

 

📍 Spread Operator

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

const copy = {...obj};

copy.b.c = 3;

console.log(obj); // { a: 1, b: { c: 3 } }
console.log(obj.b.c === copy.b.c); // true
  • Spread Operator 도 마찬가지로 2 depth 이상부터는 얕은 복사가 된다.

 


 

[해결 방법] 다차원 데이터 복사 O

📍 JSON 의 stringify() 와 parse()

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

const copy = JSON.parse(JSON.stringify(obj));

copy.b.c = 3;

console.log(obj); // { a: 1, b: { c: 2 } }
console.log(obj.b.c === copy.b.c); // false
  • JSON.stringify()객체JSON 문자열로 변환한다.
  • JSON.parse()JSON 문자열을 다시 객체로 변환한다.
  • 2 depth 이상까지도 깊은 복사가 된다.

 

❓왜 다차원 복사가 되는 것일까

  • JSON.stringify() 의 과정에서 객체의 구조와 데이터만을 문자열로 변환하고, 참조 정보는 포함되지 않는다.
  • 따라서 JSON.parse() 의 과정에서 새롭게 생성된 객체는 원본과 구조와 데이터가 동일하지만, 완전히 새로운 인스턴스이다.

 

❗️주의할 점

  • JSON.stringify()JSON.parse()순수한 JSON 데이터만 처리할 수 있다.
  • 따라서 함수정규 표현식JSON 형식에 맞지 않는 값들은 제대로 복사되지 않는다. (undefined 로 처리됨)

 

❗️❗️structuredClone() 사용해 보기

  • JSON.stringify 와 JSON.parse 를 간단하게 할 수 있는 함수인 structuredClone() 함수가 등장했었다.
  • 최근에는 주요 브라우저에서 지원되기 때문에 상황을 고려하여 사용해보면 좋을 것 같다.

 

📍 커스텀 재귀 함수

const deepCopy = (obj) => {
  if (obj === null || typeof obj !== 'object') return obj;
  
  let copy = {};
  for (let key in obj) {
    copy[key] = deepCopy(obj[key]);
  }
  return copy;
};

const obj = {
  a: 1,
  b: { c: 2 },
  func: function () {
    return this.a;
  }
};

const copy = deepCopy(obj);

copyObj.b.c = 3;
console.log(obj); // { a: 1, b: { c: 2 }, func: [Function: func] }
console.log(obj.b.c === copy.b.c); // false
  • deepCopy() 함수에 객체가 아닌 매개변수를 전달했을 경우에는 그냥 obj 객체를 반환한다.
  • 객체인 경우 객체의 값 만큼 루프를 돌고, 재귀를 호출하여 복사된 값을 반환한다.
  • 함수도 제대로 복사가 된다.

 

📍 lodash 라이브러리의 cloneDeep()

: lodash 라이브러리의 cloneDeep() 은 간편하게 깊은 복사를 구현할 수 있지만,

용량이 너무 많다는 단점(lodash 전체 라이브러리를 사용할 경우 25K 필요)이 있다.

(→ 더 나은 성능을 제공하는 네이티브 솔루션을 선택하는 것이 좋음)

728x90

'배움 기록 > JavaScript' 카테고리의 다른 글

[JS] || 와 ?? 연산자의 차이  (0) 2024.04.21

댓글