얕은 복사와 깊은 복사

햄찌·2022년 11월 18일
0

1. 얕은 복사(Shallow Copy)

얕은 복사(Shallow Copy)는 참조값의 복사를 나타낸다.

const obj = { a: 1 };
const copyObj = obj;

copyObj.a = 2;

console.log(obj.a); // 2
console.log(obj === copyObj); // true

obj라는 객체를 copyObj라는 객체에 복사하여 copyObj.a값을 변경하였더니 기존의 obj.a값도 같이 변경되었다.

마찬가지로 두 객체를 비교해봐도 true가 나온다.

이렇게 자바스크립트의 참조 타입은 얕은 복사(Shallow Copy)가 된다고 볼 수 있으며,

이는 데이터가 그대로 생성되는 것이 아닌 해당 데이터의 참조 값을 전달하여 한 데이터를 공유하는 것이다.

2. 깊은 복사 (Deep Copy)

깊은 복사는 얕은 복사(Shallow Copy)처럼 참조값이 복사되는 것이 아니라, 값만 복사되는 것을 의미한다.

const a = 1;
const b = a;

b = 2;

console.log(a); // 1
console.log(b); // 2
console.log(a === b); // false

위 코드는 변수 a 에 1을 할당하고, 변수 b 에 a 의 값을 할당한 코드이다.

변수 b 의 값을 변경하여도 기존의 a 의 값은 변경되지 않는다.

두 값을 비교하여도 false가 출력되며 서로의 값은 단독으로 존재한다는 것을 알 수 있다.

위 예제처럼 자바스크립트의 원시 타입(String , Boolean, number ...)은 깊은 복사(Deep Copy)가 되며,

이는 독립적인 메모리에 값 자체를 할당하여 생성하는 것이라고 볼 수 있다.

💡 객체의 깊은 복사
객체를 그대로 복사하여 사용하면 기존 객체의 데이터가 변경될 수 있기 때문에

객체의 값을 복사하여 사용하고 싶다면 얕은 복사(Shallow Copy)가 아닌 깊은 복사(Deep Copy)를 사용하는 것이 좋다.

Deepcopy하는 방법

1. json문법을 사용한 deep copy

const newObject = JSON.parse(JSON.stringify(oldObject));
object를 stringify -> parse 과정을 통한 deep copy인데 함수를 포함하는 경우에는 복사되지 않는다.

ex)

- 함수를 포함하는 경우:

const oldObject = {a:'1',b: () => {console.log('hi');}};
const newObject = JSON.parse(JSON.stringify(oldObject));
console.log(newObject); // {a: "1"}

2. spread를 사용한 deep copy

ES6 spread 문법을 사용해서도 deep copy가 가능하나 1 level(dimension)의 array, object에서만 deep copy가 가능하다.

ex)

  • 1차원 array에서는 deep copy 가능:
const foo = [1,2,3];
const bar = [...foo]; // deepcopy
- 2~n차원 array에서는 shallow copy가 됨:

const foo = [[1,2],3];
const bar = [...foo];
foo[0].push(3);
console.log(bar); // [[1,2,3],3]

이후 소개할 lodash의 deepCopy 방법보다 성능은 더 좋아서 1차원의 object, array만 다루는 상황이면 사용해볼 만한 것 같다.

lodash deepCopy vs spread deepCopy benchmark

  1. deep copy 함수 만들어 사용하기
    reduce와 재귀를 통해서 2차원 이상의 object, array에서도 새로운 객체를 반환하게끔 함수를 만들어 deep copy할 수 있다.stack overflow
    다음은 출처 코드로 object, array, Date 객체까지 deep copy한 결과를 반환해주는 함수다.
function deepCopy(obj) {
    if(typeof obj !== 'object' || obj === null) {
        return obj;
    }

    if(obj instanceof Date) {
        return new Date(obj.getTime());
    }

    if(obj instanceof Array) {
        return obj.reduce((arr, item, i) => {
            arr[i] = deepCopy(item);
            return arr;
        }, []);
    }

    if(obj instanceof Object) {
        return Object.keys(obj).reduce((newObj, key) => {
            newObj[key] = deepCopy(obj[key]);
            return newObj;
        }, {})
    }
}

이후 소개할 lodash를 사용한 deep copy와 비교했을때, lodash를 사용하지 않는 환경이라면 이 방법을 사용해도 좋을 것 같다. deep copy만을 위해 사이즈 큰 lodash를 import 하기에는 비효율적이라고 생각한다.

몇 가지 모듈 크기 비교 시각화
4. lodash의 cloneDeep 사용하기
lodash에서 cloneDeep이라는 함수로 간단하게 deep copy를 할 수 있다.
가장 간단하고 명확한 방법으로 대체적으로 해당 방법을 추천한다.

import _ from "lodash";

const newObj = _.cloneDeep(obj);

0개의 댓글