ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • JS deep copy 방법
    javascript 2021. 6. 6. 00:25

    시작하며

    js sortable 라이브러리를 사용하는 중 deep copy(깊은 복사)를 필요로 하는 상황이 있었다. 그런데 spread 문법을 통해 (완벽한) deep copy가 가능할 거라고 생각했었는데 이 부분에서 오류가 있었다는 것을 알게 되었고, deep copy에 대해 정리해보기로 하였다.
    총 4가지 방법으로 (1. json, 2. spread, 3. custom function, 4. lodash) deep copy하는 방법을 소개하고, 어떤 상황에서 각 방법을 사용하면 좋을지 간단히 정리했다.

    참고로 deep copy(깊은 복사)와 shallow copy(얕은 복사)의 차이에 대해서는 JS deep copy(깊은 복사) vs shallow copy(얕은 복사) 에서 정리하겠다.

    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

     

    Benchmark: Lodash cloneDeep VS spread operator - MeasureThat.net

     

    measurethat.net

     

    3. deep copy 함수 만들어 사용하기

    reduce와 재귀를 통해서 2차원 이상의 object, array에서도 새로운 객체를 반환하게끔 함수를 만들어 deep copy할 수 있다.
    다음은 stack overflow출처 코드로 object, array, Date 객체까지 deep copy한 결과를 반환해주는 함수다.

     

    Deep copy in ES6 using the spread syntax

    I am trying to create a deep copy map method for my Redux project that will work with objects rather than arrays. I read that in Redux each state should not change anything in the previous states.

    stackoverflow.com

    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);

     

    마치며

    개인적으로는 deep copy를 사용할 때 항상 lodash를 사용할 것 같다. lodash의 다양한 함수들을 기존부터 사용하다보니 프로젝트를 구성할 때 항상 포함시킬 것이고, 성능 측면에서는 다른 방법이 좋을 수 있지만 사용 가능한 범위가 넓고 간단하다는 점이 나에게는 가장 큰 장점으로 느껴진다.

    'javascript' 카테고리의 다른 글

    JS e.preventDefault()와 e.stopPropagation()  (0) 2021.06.20
    JS Optional Chaining  (0) 2021.06.20
    JS deep copy(깊은 복사) vs shallow copy(얕은 복사)  (0) 2021.06.07
Designed by Tistory.