ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 깊은 복사와 얕은 복사에 대한 고찰(?)
    IT 지식 2021. 4. 25. 19:21
    728x90

    이번에 회사 프로젝트를 진행하던 중 리덕스에서 오류가 발생했습니다. 제가 readonly형식의 데이터에 다른 데이터를 덧씌우려고 했다는 오류 메세지였습니다. 그래서 이를 해결하기 위해 여러 자료를 찾아보던 중 제가 리덕스 상태 값에 얕은 복사를 한 데이터를 덮어 씌우려고 해서 발생한 에러임을 알게 되었습니다. 그래서 이 오류를 정리할 겸 해서 오늘은 깊은 복사와 얕은 복사에 대해서 알아보도록 하겠습니다. 

     

    0. 객체 복사

     

    오늘의 주제를 말하기에 앞서 오늘 말하는 복사라는 행위는 객체를 복사하는 행위임을 말씀드립니다. 자바스크립트에서 배열은 특수한 형태의 객체이기 때문에, 배열의 복사 역시 오늘의 주제 범위에 포함됨을 말씀드립니다.

     

    1. 얕은 복사란?

     

    얕은 복사란 객체를 복사할 때 원래 값과 복사된 값이 같은 참조를 가리키고 있는 경우를 말합니다. 객체 안에 객체가 존재하는 경우, 그 안의 객체 중 하나라도 원본의 객체를 참조하고 있다면 이를 얕은 복사라고 합니다.

     

    여기서 말하는 참조라는 것은 자바스크립트가 데이터를 생성할 때 그 데이터를 메모리에 저장하는 것과 관련이 있습니다. 자바스크립트는 선언한 데이터 등을 메모리에 저장하고 이를 주소값으로 참조하여 필요한 경우에 활용하는 언어입니다. 즉, 위에서 말한 '원래 값과 복사된 값이 같은 참조를 가리키고 있다는 것은, 원래 생성한 객체와 이를 복사하여 새로 생성한 객체가 메모리의 같은 주소를 활용하고 있다는 뜻입니다. 따라서 원본 객체의 데이터를 바꿀 경우, 새롭게 복사된 객체도 이 변화에 영향을 받게 됩니다. 따라서 얕은 복사를 활용할 경우, 복사된 데이터로 인해 오류가 발생하지 않도록 신경을 써주어야 합니다. 

     

    1.1 얕은 복사를 하는 방법

     

    1.1.1 object.assign()

     

    object.assign()은 인자를 두개로 받는 메소드입니다. 이 메소드는 첫번째 요소로 들어온 객체에 다음 인자로 들어온 객체를 복사해줍니다.

    const obj = {
      a: 1,
      b: {
        c: 2,
      },
    };
    
    const copiedObj = Object.assign({}, obj);
    
    copiedObj.b.c = 3
    
    obj === copiedObj // false
    obj.b.c === copiedObj.b.c // true
    
    //출처: https://velog.io/@th0566/Javascript-%EC%96%95%EC%9D%80-%EB%B3%B5%EC%82%AC-%EA%B9%8A%EC%9D%80-%EB%B3%B5%EC%82%AC

     

    1.1.2 spread 연산자(전개 연산자)

     

    전개 연산자는 es6에 새로이 추가된 메소드입니다. 이 연산자를 활용하면 객체를 쉽게 복사할 수 있습니다.

    const obj = {
      a: 1,
      b: {
        c: 2,
      },
    };
    
    const copiedObj = {...obj}
    
    copiedObj.b.c = 3
    
    obj === copiedObj // false
    obj.b.c === copiedObj.b.c // true
    
    //출처: https://velog.io/@th0566/Javascript-%EC%96%95%EC%9D%80-%EB%B3%B5%EC%82%AC-%EA%B9%8A%EC%9D%80-%EB%B3%B5%EC%82%AC

     

    2. 깊은 복사

     

    깊은 복사란 얕은 복사와는 달리 참조 값을 공유하지 않는 복사를 말합니다. 따라서 원본 객체값의 데이터가 변화한다고 해도 복사된 객체의 데이터는 영향을 받지 않게 됩니다. 하지만 이 방식은 메모리에 새로운 데이터를 할당한다는 측면에서 메모리의 효율성이 떨어지고(코드의 성능 저하로 이어짐), 코드의 가독성을 저해할 수 있다는 점에서 필요한 경우가 아니라면 그다지 추천하지 않는 복사 방법입니다. 깊은 복사를 하는 방법은 다음과 같습니다.

     

    2.1.1 재귀함수를 이용한 복사

    const obj = {
      a: 1,
      b: {
        c: 2,
      },
    };
    
    function copyObj(obj) {
      const result = {};
    
      for (let key in obj) {
        if (typeof obj[key] === 'object') {
          result[key] = copyObj(obj[key]);
        } else {
          result[key] = obj[key];
        }
      }
    
      return result;
    }
    
    const copiedObj = copyObj(obj);
    
    copiedObj.b.c = 3
    
    obj.b.c === copiedObj.b.c //false 
    
    //출처: https://velog.io/@th0566/Javascript-%EC%96%95%EC%9D%80-%EB%B3%B5%EC%82%AC-%EA%B9%8A%EC%9D%80-%EB%B3%B5%EC%82%AC

     

     

     

    2.1.2 JSON.stringify()

     

    JSON.stringify()는 객체를 JSON 형식으로 변환하는데, 이 과정에서 원본 객체와의 모든 참조가 끊어지게 됩니다. 객체를 JSON 형식으로 변환 후 JSON.parse()로 다시 자바스크립트 객체로 만들어 주면 깊은 복사가 됩니다. 하지만 이 방법은 다른 방법들에 비해 아주 느리기 때문에 제일 추천드리지 않는 방법입니다.

     

    const obj = {
      a: 1,
      b: {
        c: 2,
      },
    };
    
    const copiedObj = JSON.parse(JSON.stringify(obj));
    
    copiedObj.b.c = 3
    
    obj.b.c === copiedObj.b.c //false 
    
    //출처: https://velog.io/@th0566/Javascript-%EC%96%95%EC%9D%80-%EB%B3%B5%EC%82%AC-%EA%B9%8A%EC%9D%80-%EB%B3%B5%EC%82%AC

     

    2.1.3 lodash 라이브러리 사용

     

    lodash는 전세계 자바스크립트 개발자들이 많이 사용하는 라이브러리입니다. 그만큼 유용한 기능들을 많이 가지고 있는데요, 그 중의 하나가 cloneDeep()이라는 메소드입니다.

    const obj = {
      a: 1,
      b: {
        c: 2,
      },
    };
    
    const copiedObj = _.cloneDeep(obj);
    
    copiedObj.b.c = 3
    
    obj.b.c === copiedObj.b.c //false
    
    //출처: https://velog.io/@th0566/Javascript-%EC%96%95%EC%9D%80-%EB%B3%B5%EC%82%AC-%EA%B9%8A%EC%9D%80-%EB%B3%B5%EC%82%AC

    lodash를 사용하면 위와 같이 깊은 복사를 간단하게 할 수 있습니다.

     

    3. 마무리

     

    이번 복사 포스팅을 준비하기 위해 다양한 글들을 공부해본 결과, 제가 프로젝트에서 활용한 방식이 그다지 좋은 방식이 아니라는 것을 알게 되었습니다. 또한 상황에 따라 깊은 복사와 얕은 복사를 자유자재로 쓸 수 있어야 자바스크립트의 기본은 할 수 있다고 말할 수 있다는 점이 굉장히 인상 깊었습니다. 오늘의 포스팅은 여기서 마무리 되지만, 다음에 이 주제로 포스팅을 하게 된다면 좀 더 퀄리티 있는 글을 쓸 수 있도록 더 정진해야겠습니다.

     

    728x90

    댓글

Designed by Tistory.