[JS] 깊은 복사와 얕은 복사

BitedRadish·2024년 1월 14일

⭐️ 개요

MDN에서 filter() , concat() , slice() 등 Array method나 객체 타입과 관련 document를 볼 때 반환 값에 얕은 복사라는 말을 자주 볼 수 있습니다. 얕은 복사란 무엇이고 , 깊은 복사란 무엇일까요 ?

자바스크립트에서 복사는 변수 또는 객체의 값을 다른 변수나 객체로 복제하는 작업을 의미합니다. 이 복사에서 얕은 복사와 깊은 복사라는 두 가지 유형으로 나뉩니다.

얕은 복사와 깊은 복사
객체를 프로퍼티 값으로 갖는 객체의 경우 얕은 복사는 한 단계까지만 복사하는 것을 말하고 , 깊은 복사는 객체에 중첩되어 있는 객체까지 모두 복사하는 것을 뜻합니다.

얕은 복사와 깊은 복사에 대한 설명에 객체란 단어가 자주 등장하고 있습니다.
두 개념을 자세히 알아보기 전에 객체란 무엇인지에 대해 먼저 알아볼까요 ?

⭐️ 원시 값과 객체

자바스크립트에서 원시 값과 객체는 서로 상반되는 개념입니다.
원시 값 (문자(string) , 숫자(number) , bigint , 불린(boolean) , 심볼(symbol) , null , undefined 형)은 변수에 할당하면 변수에는 실제 값이 저장됩니다. 이에 비해 객체(배열 , 함수 등)를 변수에 할당하면 변수에는 참조 값이 저장됩니다.
따라서 , 원시 값과 객체는 변수에 저장하는 값이 다르기 때문에 변수에 저장된 값을 다른 변수에 복사 할때에 저장되는 방식이 다릅니다.

let score = 80;
const copy = score;

console.log(score); // 80
console.log(copy); // 80

score = 100;

console.log(score); // 100
console.log(copy); // 80

해당 코드는 원시 값이 저장된 score 변수를 copy에 복사 해주었습니다. score 변수는 실제 값을 저장하고 있기 때문에 실제 값인 80을 copy에 넘겨주어 원본 변수(score) 값이 변경되지 않았습니다.

const arr = [1, 2, 3, 4];
const copyArr = arr;

copyArr[1] = "a";

console.log(arr); // [ 1, 'a', 3, 4 ]
console.log(copyArr); // [ 1, 'a', 3, 4 ]

하지만 , arr와 copyArr는 동일한 메모리 주소를 기억하고 있고 , 동일한 참조 값에 접근하고 있기 때문에 복사본의 원소 값을 변경하니 원본 값도 변경되었습니다.

이와 같이 , 객체를 복사할 때 원본 객체의 값을 변경하는 문제를 해결하기 위한 방법에 얕은 복사와 깊은 복사 두 가지 유형이 등장하게 된 것입니다.

⭐️ 얕은 복사 (Shallow Copy)

배열의 경우는 Array.from() , 객체의 경우에는 Object.assign() , 스프레드 연산자 (...) , Array의 메소드 등을 이용하여 얕은 복사가 가능합니다. 이의 문제점은 객체를 프로퍼티 값으로 갖는 객체의 경우 한 단계까지만 복사한다는 것입니다.

// 얕은 복사
const o = { x: { y: 1 } };

const c1 = { ...o };

console.log(c1 === o); // false
console.log(c1.x === o.x); // true

c1과 o (1 depth)는 다른 메모리 값을 참조하고 있지만 , c1.x와 o.x (2 depth)는 같은 메모리 값을 참조하고 있습니다.

이런 얕은 복사는 원본 객체의 값을 변경하는 등 예측하기 힘든 결과를 낳을 수 있기 때문에 , 이를 해결하고자 깊은 복사가 필요한 것입니다.

⭐️ 깊은 복사 (Deep Copy)

// 깊은 복사
const _ = require("lodash");
const c2 = _.cloneDeep(o);
console.log(c2 === o); // false
console.log(c2.x === o.x); // false

깊은 복사는 얕은 복사와 달리 객체에 중첩되어 있는 객체까지 모두 복사할 수 있어 , 원본 객체의 값을 변경하는 불상사를 방지할 수 있습니다.

JSON.stringify()과 JSON.parse()를 이용한 깊은 복사는 일반적으로 단순한 객체나 배열에 대해 잘 작동하지만, 함수나 프로토타입, undefined 등을 포함한 특정 데이터 타입에 대해서는 제대로 동작하지 않을 수 있습니다. 그렇기 때문에 lodash의 clondeep()을 이용하는 것을 권장하고 있습니다.

참고 자료 : Modern Javascript deep dive

profile
코딩 주니어

0개의 댓글