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

정진우·2024년 5월 28일
1

JavaScript

목록 보기
15/20
post-thumbnail

  • 자바스크립트는 값에 의한 전달(passed by value) 이 일어나는 5가지의 데이터타입(BooleanNullUndefinedStringNumber)을 가지고 있습니다. 우리는 이러한 데이터 타입을 원시 타입(Primitive Types) 이라고 부릅니다.
  • 또 자바스크립트는 참조에 의한 전달(passed by reference) 이 일어나는 3가지의 데이터 타입(ArrayFunctionObject)도 가지고 있습니다. 사실 이 3가지는 크게 보자면 전부 객체(Objects)로 볼 수 있습니다.

얕은 복사와 깊은 복사?

얕은 복사

  • 원본 객체의 참조를 복사해서 새로운 객체를 만듭니다. 즉, 원본 객체와 복사된 객체가 동일한 메모리 주소를 참조하게 됩니다.
  • 원본 객체나 배열의 내부 구조를 변경하면 복사본도 같이 변경될 수 있습니다.

깊은 복사

  • 원본 데이터의 값을 복사하는 것으로, 원본 데이터와 복사본은 서로 다른 메모리 주소를 참조합니다.
  • 복사된 값은 원본 값과 완전히 독립적인 메모리 위치를 참조하게 됩니다.
  • 깊은 복사의 경우, 복사된 값이 원본과 완전히 분리되어 있기 때문에 원본 값의 변경이 복사본에 영향을 미치지 않습니다.

원시값은 값을 복사할 때, 복사된 값을 다른 메모리에 할당하므로 원래 값과 복사된 값이 서로에게 영향을 미치지 않습니다.

const a = 1;
let b = a;
  
b = 2;
  
console.log(a); // 1
console.log(b); // 2
  • ba의 값을 복사합니다. ba와 동일한 값 1을 갖게 됩니다.
  • 이 때, ba와는 다른 메모리 공간에 할당됩니다. 즉, ba와 독립된 값을 갖게 됩니다.
  • b에 새로운 값 2를 할당합니다. 이는 b의 값을 변경하지만, a의 값에는 영향을 미치지 않습니다.

그러나 참조 값은 변수가 객체의 주소를 가리키는 값이므로, 복사된 값(주소)은 동일한 객체를 가리킵니다.

const num = [1, 2, 3];
const numCopy = num; // 얕은 복사

numCopy.push(4);
const result = num === numCopy;

console.log(num); // 출력: [ 1, 2, 3, 4 ]
console.log(numCopy); // 출력: [ 1, 2, 3, 4 ]
console.log(result); // true
  • 변수 num은 실제로 값을 가진게 아니라 배열([1, 2, 3])이 위치한 메모리의 주소를 가리킵니다.
  • numCopy 변수는 num 변수와 동일한 메모리 주소를 참조합니다. 따라서 numnumCopy는 같은 배열을 참조하게 됩니다. 이 경우, 이는 얕은 복사입니다.
  • 원본 배열 num에도 영향을 미칩니다.
  • numCopy를 통해 배열에 4를 추가하면, 실제로는 같은 메모리 주소를 참조하고 있기 때문에 num도 영향을 받아 [1, 2, 3, 4] 로 변경됩니다.
  • numnumCopy는 동일한 메모리 주소를 참조하고 있기 때문에, === 연산자는 true를 반환합니다.

얕은 복사 예제

Object.assign()

Object.assign()은 자바스크립트에서 사용되는 내장 함수 중 하나입니다. 이 함수는 한 개 이상의 소스 객체로부터 대상 객체로 속성을 복사 할 때 사용됩니다.

const user = {
  name: "jinwoo",
  age: 123,
  email: ["1234@gmail.com"],
};

// 원본 객체를 복사해서 새로운 객체를 생성
const copy = Object.assign({}, user);
console.log(copy === user); // false

// 복사본을 변경
copy.age = 900;
copy.email.push("5678@gmail.com");

console.log("user", user);
console.log("copy", copy);

console.log(user.email === copy.email); // true

/*
false

user {
  name: 'jinwoo',
  age: 123,
  email: [ '1234@gmail.com', '5678@gmail.com' ]
}
copy {
  name: 'jinwoo',
  age: 900,
  email: [ '1234@gmail.com', '5678@gmail.com' ]
}

true
*/
  • Object.assign({}, user)를 사용하면 객체의 첫 번째 레벨만 복사되어 새로운 객체가 생성되는데, 이를 얕은 복사(shallow copy)라고 합니다. 객체의 속성 중에서 배열이나 다른 객체와 참조 타입의 속성은 원본 객체와 복사된 객체가 같은 메모리 주소를 참조하게 됩니다.
  • copy.email.push("5678@gmail.com") 코드를 실행하면 원본 객체의 email 속성도 변경되었고, user.email === copy.email의 결과가 true가 나온 것입니다. 이런 현상을 피하려면 깊은 복사(deep copy)를 사용해야 합니다.

전개 연산자(...)

배열이나 객체의 요소들을 개별적으로 분리하여 다른 배열이나 객체에 복사하는 데 사용됩니다.

const user = {
  name: "jinwoo",
  age: 123,
  email: ["1234@gmail.com"],
};

// 원본 객체를 복사해서 새로운 객체를 생성
const copy = { ...user };
console.log(copy === user); // false

// 복사본을 변경
copy.age = 900;
copy.email.push("5678@gmail.com");

console.log("user", user);
console.log("copy", copy);

console.log(user.email === copy.email); // true

/*
false

user {
  name: 'jinwoo',
  age: 123,
  email: [ '1234@gmail.com', '5678@gmail.com' ]
}
copy {
  name: 'jinwoo',
  age: 900,
  email: [ '1234@gmail.com', '5678@gmail.com' ]
}

true
*/

깊은 복사 예제

lodash라이브 러리의 cloneDeep함수를 사용해서 깊은 복사를 진행했습니다. _.cloneDeep() 함수는 재귀적으로 객체를 순회하면서 모든 하위 객체들을 새로운 객체로 복사한다고 합니다. 그래서 원본 객체의 속성을 변경해도 복사본 객체에 영향을 주지 않는다고 합니다. 아직 재귀함수에 대해 배우지 않아서 재귀함수를 배우고 재귀관련 내용을 추가하겠습니다.

import _ from "lodash";

const user = {
  name: "jinwoo",
  age: 123,
  email: ["1234@gmail.com"],
};

// 원본 객체를 복사해서 새로운 객체를 생성
const copy = _.cloneDeep(user);
console.log(copy === user); // false

// 복사본을 변경
copy.age = 900;
copy.email.push("5678@gmail.com");

console.log("user", user);
console.log("copy", copy);

console.log(user.email === copy.email); // false

/*
false

user { name: 'jinwoo', age: 123, email: [ '1234@gmail.com' ] }

copy {
  name: 'jinwoo',
  age: 900,
  email: [ '1234@gmail.com', '5678@gmail.com' ]
}

false
*/
  • console.log(copy === user) 의 결과는 false입니다. 이는 깊은 복사를 통해 복사된 객체와 원본 객체가 서로 다른 객체임을 의미합니다.
  • console.log(user.email === copy.email)의 결과도 false입니다. 깊은 복사를 통해 복사된 객체들이 서로 다른 배열의 메모리 주소를 참조 하고있습니다.

요약

  • 깊은 복사(Deep Copy)는 '실제 값'을 새로운 메모리 공간에 복사하는 것을 의미하며,
  • 얕은 복사(Shallow Copy)는 '주소 값'을 복사한다는 의미입니다.

참고

profile
내가 바뀌지 않으면 아무것도 바뀌지 않는다 🔥🔥🔥

0개의 댓글