[Javascript] 얕은 복사(Shallow Copy)와 깊은 복사(Deep Copy)

hyeojung·2022년 1월 17일
0

Javascript

목록 보기
2/2
post-thumbnail

프로젝트를 하다 배열을 복사해서 써야 할 일이 생겼다.
그래서 그냥 아무 생각 없이 일반 변수처럼 대입해서 쓰면 되겠지! 라고 생각하며 다음과 같이 썼다.


// 복사할 객체
const src = [ val1, val2, val3, ...];

// 이렇게 복사하면 안 되는 거였냐구 ㅜㅜ
const dest = src;

그런데 이렇게 복사하고 나서 dest의 값을 임시로 변경했더니 src의 값까지 변경되어, 원본 데이터를 잃어버리는 매우매우 끔찍한 상황이 발생했다.

왜 이런 일이 일어난 걸까?


👀 원시 타입과 참조 타입

💡 참고: 이전 포스팅 원시 타입(Primitive Type)과 참조 타입(Reference Type)

결론부터 얘기하자면 위와 같은 일이 일어난 이유는 src가 참조 타입이기 때문이다.
원시 타입과 참조 타입에 대한 자세한 내용은 위 링크 참고 😊

참조 타입의 변수들은 데이터 복사가 일어날 때 값이 담긴 주솟값을 바로 복사하지 않고, 값이 담긴 주솟값들로 이루어진 묶음을 가리키는 주솟값을 복사한다.

그렇다면 참조 타입 데이터를 완벽하게(?) 복사해, 복사된 값을 바꾸어도 원본이 변경되지 않게 할 수는 없는 걸까?


💡 얕은 복사(Shallow Copy)와 깊은 복사(Deep Copy)

위 질문에 대한 답은 당연히 할 수 있다!이다.
그렇다면 어떻게 복사해야 하는 걸까..? 이에 대해 알아보기 전에 먼저 javascript에서 값이 복사되는 두 가지 방식에 대해 알아보자.

참조형 데이터의 얕은 복사와 깊은 복사

✔️ 얕은 복사 (Shallow Copy)

➡️ 바로 아래 단계의 값만 복사. 즉 참조형 데이터인 객체(Object)의 경우 객체가 가리키는 값들의 묶음을 가리키는 주솟값만 복사 !

얕은 복사로 데이터를 복사 후 복사본에서 프로퍼티 값을 바꾸면,
복사본과 원본은 모두 같은 객체를 바라보고 있는 상태에서 프로퍼티가 변경되는 것이므로 원본의 값도 변하게 된다.

원시 타입 데이터의 경우 값이 담긴 데이터 공간의 주소가 변수 공간에 바로 저장되어 있기 때문에,
값을 복사한 후 복사본의 값을 바꾸면 복사본이 새로운 값의 주소를 저장하게 되므로 원본은 변하지 않는다.
다시 말해 원시 타입 데이터는 얕은 복사와 깊은 복사의 결과가 동일하다. 고려할 필요 xx

✔️ 깊은 복사 (Deep Copy)

➡️ 내부의 모든 값들을 하나하나 찾아 전부 복사. 객체 안에 객체가 있을 경우에도 내부 프로퍼티들을 모두 순회하여 얕은 복사가 이루어지지 않도록 해야 함

이 경우 원본과 복사본이 서로 다른 객체를 가리키게 되므로 복사본의 프로퍼티가 변경되어도 원본이 변경되지 않는다.

예를 들어 보자.

var copyObjectDeep = function(target) {
  var result = {};
  if (typeof target === 'object' && target !== null) {
    for (var prop in target) {
      result[prop] = copyObjectDeep(target[prop]);
    }
  } else {
    result = target;
  }
  return result;
}

위 코드는 매개변수로 들어온 target이 null이 아닌 객체일 경우 깊은 복사를 수행해주는 함수이다.
만약 target의 프로퍼티가 객체인 경우 (중첩 객체) 재귀를 통해 깊은 복사를 수행한다.

이를 실행해 보면,

var obj1 = {
  a: 1,
  b: {
    c: null,
    d: [1, 2]
  }
};

var obj2 = copyObjectDeep(obj1);

console.log(obj1 === obj2); // false
console.log(obj1); // { a: 1, b: { c: null, d: [1, 2] } }
console.log(obj2); // { a: 1, b: { c: null, d: { 0: 1, 1: 2 } }

✔️ 참조형 데이터의 깊은 복사 방법

  1. 사용자 정의 함수 만들기 (위의 예시 참조)
  2. 배열의 복사: Array.prototype.slice() 활용
    • 배열 내에 참조형 요소가 있다면 얕은 복사를 수행하니 주의!
  3. JSON.parse, JSON.stringify를 통한 복사
    • 객체를 json 문자열로 변환해 참조를 모두 끊은 후, 다시 객체로 변환하는 방법
    • json으로 변환할 수 없는 프로퍼티는 무시되며 속도가 매우 느리다고 함
  4. lodash 라이브러리 사용

참고자료

[Javascript] 얕은 복사, 깊은 복사 (배열 복사, 객체 복사, shallow copy, deep copy, slice, JSON.parse, JSON.stringify)
[Javascript] 얕은 복사, 깊은 복사

profile
응애 나 애기 개발자

0개의 댓글