얕은복사(Shallow Copy) vs 깊은복사(Deep Copy)

304호 하숙생·2023년 1월 19일
0

얕은복사(Shallow Copy)는 객체를 복사할 때 기존 값과 복사된 값이 같은 참조를 가리키고 있는것을 말한다.
객체 안에 객체가 있을 경우 한 개의 객체라도 기존 변수의 객체를 참조하고 있다면 이를 얕은 복사라고 한다.

깊은복사(Deep Copy)는 객체의 '실제 값'을 새로운 메모리 공간에 복사하는 것을 말하며
객체 안에 객체가 있을 경우에도 원본과의 참조가 완저히 끊어진 객체를 말한다.

자바스크립트에서 값은 원시값, 참조값 두가지가 존재한다.

  • 원시값((primitive types)은 객체가 아니면서 method를 가지지 않는 7가지 타입이 있다.
    String, Number, Bigint, Boolean, Symbol, Null, Undefined
  • 참조값 (reference types)은 원시 자료형이 아닌 나머지
    대표적으로 Array, Function, RegExp(정규식)

원시 타입 데이터는 각 변수간에 데이터를 복사할 경우, 데이터 값이 복사되기 때문에 기존의 데이터에 영향이 가지 않는다.

  const A = "A";
  let B = A;
  B = "C";
  console.log(A); // A
  console.log(B); // C

참조 타입 데이터는 참조값(주소값)을 복사하기 때문에 복사한 주소안에 있는 데이터를 변경하게되면 기존의 데이터에도 영향이 간다.

 const A = ["a", "b"];
  let B = A;
  B[0] = "c";
  console.log(A); // ['c', 'b']
  console.log(B); // ['c', 'b']

이러한 object의 특징 때문에 object를 복사하는 방법은 크게 두가지로 나뉜다.

얕은 복사 Shallow Copy

얕은 복사란 object를 복사할 때 위의 예제처럼 원래값과 복사된 값이 같은 참조를 가리키고있는 것을 말한다.
object안에 object가 있을 경우 한개의 객체라도 원본 객체를 참조하고 있다면 이를 얕은복사 라고 한다.

얕은 복사를 하는 방법

  1. Object.assing() MDN
    Object.assing()을 이용하면 객체 자체는 깊은복사가 수행되지만, 2차원 이상의 객체는 얕은 복사가 수행된다.
    아래 예시에서 객체는 서로 다른 주소를 참조하고 있어 깊은 복사가 이루어졌지만 내부의 객체는 같은 주소를 참조하고있다.
  const obj = { name: "kim", age: { number: 20 } };
  let copy = Object.assign({}, obj);
  // 복사한 객체 수정
  copy.age.number = 25;

  console.log(obj, copy);
  // { name: "kim", age: { number: 25 } }, { name: "kim", age: { number: 25 } }
  console.log(obj === copy); 
  // 객체 자체는 깊은 복사, false
  console.log(obj.age.number === copy.age.number); 
  // 2차원 객체는 얕은 복사, 같은 주소를 참조하고있어 true
  1. Slice() MDN
const obj = [1,2,3]
const copy = obj.slice()
// 복사된 객체 수정
copy.push(4)

console.log(obj, copy) // [1,2,3], [1,2,3,4]

기존 배열에 영향을 끼치지 않아 깊은 복사로 볼수 있지만, 원시값을 저장한 1차원 배열일 뿐이다.
원시값은 기본적으로 깊은 복사이며 slice()메소드는 기본적으로 얕은 복사를 수행한다.
1차원 배열이 아닌 2차원 배열 일때를 보자

const obj = [1,[1]]
const copy = obj.slice()
// 복사된 객체 수정
copy[1][0] = 2

console.log(obj, copy) // [1, [2]], [1, [2]]

1차원 배열이 아닌 중첩 구조를 갖는 2차원 배열의 객체를 수정하게되면 얕은 복사를 수행하게 된다.

const obj = [1, [1]]
const copy = obj.slice()
// 복사된 객체 수정
copy[0] = 2
copy[1][0] = 2

console.log(obj, copy) // [1, [2]], [2, [2]]

또다른 예시입니다. 배열 안에 객체를 수정하고자 할 경우 얕은 복사를 수행하는 것을 볼 수 있다.
하지만 원시값은 기본적으로 깊은 복사라 기존 변수에 있는 값과는 다른 값을 도출하는 것을 볼 수 있다.

  1. Spread 연산자(전개 구문)
    전개 구문도 assing()과 같이 복사한 객체 자체는 깊은 복사 이지만 내부의 객체는 얕은 복사가 진행된다.
  const obj = { name: "kim", age: { number: 20 } };
  const copy = {...obj}
// 복사된 객체 수정
copy.age.number = 25

console.log(obj, copy);
  // { name: "kim", age: { number: 25 } }, { name: "kim", age: { number: 25 } }
  console.log(obj === copy); 
  // 객체 자체는 깊은 복사, false
  console.log(obj.age.number === copy.age.number); 
  // 2차원 객체는 얕은 복사, 같은 주소를 참조하고있어 true

깊은 복사 Deep Copy

깊은 복사된 객체는 객체 안에 객체가 있을 경우에도 원본과의 참조가 완전히 끊어진 객체를 말한다.

  1. JSON.stringify / JSON.parse
    JSON.stringify()는 개체를 json문자열로 변환하는데 이 과정에서 원본 객체와의 참조가 모두 끊어진다.
    객체를 문자열로 변환 후, JSON.parse()를 이용해 다시 원래 객체로 만들어주면된다.
    깊은 복사를 하는방법중에 가장 쉽지만 다른 방법에 비해 느리다는점과 객체가 function인 경우, undefined로 처리한다는 것이 단점이다.
const obj = [1, [1]]
const copy = JSON.parse(JSON.stringify(obj))
// 복사된 객체 수정
copy[1][0] = 2

console.log(obj, copy) // [1, [1]], [1 ,[2]]
  1. 재귀 함수
    재귀 함수 copyObj를 만들어 값을 복사 하였다.
  const obj = { name: "kim", age: { number: 20 } };

  function copyObj(obj) {
    let result = {};

    for (let key in obj) {
      if (typeof obj[key] === "object") {
        result[key] = copyObj(obj[key]);
      } else {
        result[key] = obj[key];
      }
    }
    return result;
  }

  let copy = copyObj(obj);

  copy.age.number = 25;

  console.log(obj, copy);
  // { name: "kim", age: { number: 25 } }, { name: "kim", age: { number: 20 } }
  console.log(obj.age.number === copy.age.number); // false

참고 사이트
Digitalocean ::: Copying Objects in JavaScript

profile
304호 하숙생의 코딩일기장

0개의 댓글