[JS] 참조 타입의 얕은 복사(Shallow copy)와 깊은 복사(Deep copy)

Wonhyun Kwon·2023년 6월 22일
0

JavaScript

목록 보기
4/5
post-thumbnail

1. 서론

이전에 [개념] Call by value, Call by reference 포스트에서 원시, 참조 값에 대해 알아보았다.

원시는 말 그대로 원본 데이터 그대로를 다른 메모리에 동일하게 복사하여 가져온 데이터이다.
참조는 원본 데이터의 메모리 주소값을 복사하고 원본 데이터를 공유하는 것이라고 배웠다.

그래서 참조값은 값이 변하면 원본 데이터가 변하기 때문에 주의가 필요하다. 그렇다면 필요에 따라 원본 데이터가 변하지 않게 복사할 수 있을까?

참조 타입의 복상 방법으로는 얕은 복사 (Shallow copy), 깊은 복사 (Deep copy) 두 가지가 있다.




2. 얕은 복사

참조 타입 데이터가 원본 데이터의 메모리 주소값을 저장한다.

예를 들어, 객체를 복사할 때 원본 값과 복사된 값이 같은 참조(=메모리 주소)를 가리키는 것이다.

다음 예제를 보자.

const obj = {
  a: 1,
};

let copy = obj;

copy.a = 2;

console.log(obj === copy); // true

copy는 obj를 복사했지만 얕은 복사로 이루어졌고, copy의 value를 바꾸면 주소를 공유하고 있는 obj 변수 역시 바뀌게 되므로 console은 true 를 반환하게 된다.




3. 깊은 복사

새로운 메모리 공간을 확보해 원본 데이터를 완전히 복사한다.

한마디로 복사된 객체는 아예 다른 메모리 공간에서 동일한 값을 가지게 된다.

다음 예제를 보자.

const obj = {
  a: 1,
};

let copy = { ...obj };

copy.a = 2;

console.log(obj === copy);

참고로 깊은 복사하는 방법은 여러 가지이다. 그 방법은 아래에서 서술하겠다.
많은 방법 중 스프레드 방식으로 깊은 복사를 한 모습이다.
예상과 같이 copy의 value를 바꿔도 아예 다른 주소이니 obj 변수에는 영향을 끼치지 않고, copy 변수는 완전히 독립적인 형태가 되었다.




4. 깊은 복사 방법

1) Object.assign()

객체를 병합하는 메서드이다.
사용 방법은 다음과 같다.

Object.assign(target, ...sources);
  • target: 병합 결과를 저장할 대상 객체
  • sources: 병합할 하나 이상의 소스 객체

소스 객체의 속성을 대상 객체로 복사한다. 소스 객체는 여러 개가 될 수 있고, 순서에 따라 대상 객체의 속성이 덮어씌워질 수 있다.

다음은 예제이다.

const obj = {
  a: 1,
};

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

console.log(obj === copy); // false

copy의 값을 바꿔도 더 이상 obj에 영향을 주지 않는다.


하지만, 객체 안에 객체는 얕은 복사가 이뤄진다.

이게 무슨 말이냐 ?
다음 예제를 보자.

const obj = {
  A: {
    a: 1,
  },
  B: 2,
};

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

copy.B = 3;

console.log(obj === copy); // false

copy.A.a = 4;

console.log(obj.A === copy.A, obj.A.a, copy.A.a); // true 4 4

copy는 마치 깊은 복사가 이뤄진 것 같지만, A와 같은 객체 안의 객체는 '얕은 복사' 가 이루어졌다.
console에서 알 수 있듯이, copy는 전체적으로 깊은 복사이지만 그 내부의 객체들은 얕은복사가 이루어졌다.

따라서, 객체 안에 또 다른 객체가 있는 경우 깊은 복사를 원할 땐 적절하지 않은 방법이다.


2) spread 문법

객체/배열을 복사하여 새로운 객체/배열을 만드는 깊은 복사 방법이다.

이는 Object.assign() 메서드와 같이 객체 안의 객체는 얕은 복사가 된다는 점이 동일하다.

다음은 사용 방법이다.

const obj = {
  a: 1,
};

let copy = { ...obj };

console.log(obj === copy); // false

이 역시 깊은 복사가 이루어져 console 결과는 false 를 나타낸다.


3) JSON.parse(), JSON.stringify()

이 방법은 객체 안의 또 다른 객체까지 전부 깊은 복사가 이루어진다.

다음은 사용 방법이다.

const obj = {
  A: {
    a: 1,
  },
  B: 2,
};

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

copy.A.a = 4;

console.log(obj.A); // { a: 1 }
console.log(copy.A); // { a: 4 }
console.log(obj.A === copy.A);

조금 복잡하지만 결론은 간단하다.
Object.assing() 메소드와 spread 문법과 다르게 모든 것이 깊은 복사가 되었다.

따라서, 특별한 경우가 아니라면 이 방법을 통해 깊은 복사하는 것을 추천한다.

profile
모든 사용자가 만족하는 UI를 만드는 FE 개발자 권원현입니다.

0개의 댓글