JS - 객체복사

sarang_daddy·2023년 4월 23일
0

Javascript

목록 보기
20/26
post-thumbnail

자바스크립트에서 원시값은 값에 의한 전달, 객체는 참조에 의한 전달 방식으로 동작한다.

함수의 매개변수 또한 함수 몸체 내부에서 변수와 동일하게 취급되므로
타입에 따라 값에 의한 전달, 참조에 의한 전달 방식을 그대로 따르게 된다.

// 매개변수 primitive는 원시 값을 전달받고, 매개변수 obj는 객체를 전달받는다.
function changeVal(primitive, obj) {
  primitive += 100;
  obj.name = 'Kim';
}

// 외부 상태
var num = 100;
var person = { name: 'Lee' };

console.log(num); // 100
console.log(person);  // {name: 'Lee'}

// 원시 값은 값 자체가 복사되어 전달되고 객체는 참조 값이 복사되어 전달된다.
changeVal(num, person);

// 원시 값은 원본이 훼손되지 않는다.
console.log(num); // 100

// 객체는 원본이 훼손된다.
console.log(person);  // {name: 'Kim'}

⛔️ 여기서 문제는 참조(객체)에 의한 전달인 경우 원본이 훼손된다는 것이다.

  • 이는 상태 변화를 추적하기 어렵게 만들고, 코드의 복잡성을 증가시킨다.
  • 객체의 변화를 추적하기 위해서는 옵저버 패턴 등을 통해 참조를 공유하는 모든 객체를 관리해야 한다.
  • 객체를 불변 객체로 만들어 사용하는 방법도 있다. (객체를 복사하여 사용)

🧐 객체를 불변 객체로 만드는 방법에 대해서 알아보도록 하자.

자바스크립트에서 객체를 불변 객체로 만들기 위해서는 객체를 복제하고,
해당 복제본을 수정하여 원래 객체가 변경되지 않도록 해야 한다.
이를 위해서는 다음과 같은 방법을 사용할 수 있다.

✅  얕은 복사(shallow copy)

Object.assign()을 사용하는 방법

Object.assign() 메서드를 사용하여 빈 객체에 원래 객체의 프로퍼티를 복사한 다음,
이를 수정하여 불변 객체를 만들 수 있다.

const originalObj = { a: 1, b: 2 };
const immutableObj = Object.assign({}, originalObj, { c: 3 });

  • 위 코드에서는 originalObj 객체를 복사하여 빈 객체와 병합하고,
    새로운 프로퍼티인 c를 추가하여 immutableObj 객체를 생성한다.
  • immutableObj의 a값을 변경해도 originalObj의 a 값은 변경되지 않는다.

Spread 연산자를 사용하는 방법

Spread 연산자를 사용하여 객체의 프로퍼티를 빈 객체에 복사한 다음,
이를 수정하여 불변 객체를 만들 수 있다.

const originalObj = { a: 1, b: 2 };
const immutableObj = { ...originalObj, c: 3 };
  • 위 코드에서는 originalObj 객체의 프로퍼티를 빈 객체에 복사하고,
    새로운 프로퍼티인 c를 추가하여 immutableObj 객체를 생성한다.

Immutable.js를 사용하는 방법

Immutable.js는 자바스크립트의 불변성을 보장하는 라이브러리다.
이를 사용하여 객체를 불변 객체로 만들 수 있다.

import { Map } from 'immutable';

const originalObj = Map({ a: 1, b: 2 });
const immutableObj = originalObj.set('c', 3);
  • 위 코드에서는 originalObj 객체를 Immutable.js의 Map 객체로 변환하고,
  • set() 메서드를 사용하여 새로운 프로퍼티인 c를 추가하여 immutableObj 객체를 생성한다.

이러한 방법을 사용하여 객체를 불변 객체로 만들면,
객체를 변경하려는 시도가 발생할 경우 에러가 발생하여 코드의 안정성을 보장할 수 있다.

⛔️ 다만, 얕은 복사에서도 객체 내부의 값들은 참조로 복제하여 새로운 객체를 생성한다.

const originalObj = { a: { b: 1 } };
const shallowCopyObj = Object.assign({}, originalObj);

shallowCopyObj.a.b = 2;

console.log(originalObj.a.b); // 2
console.log(shallowCopyObj.a.b); // 2
  • 얕은 복사는 원본 객체의 내부 값들을 참조로 가져오기에 원본 훼손의 결과를 초래할 수 있다.
  • 이를 방지하기 위해서는 깊은 복사를 통해 객체 내부의 값들도 복제하여 완전히 새로운 객체를 생성해야 한다.

✅  깊은 복사(deep copy)

원본 객체 내부의 값이 객체인 경우, 복제된 객체와 원본 객체가 같은 참조를 가지게 된다.
이 경우, 복제된 객체를 수정하면 원본 객체도 같이 수정되어 원하지 않는 결과를 초래할 수 있다.

따라서, 객체 내부의 값이 객체인 객체를 불변 객체로 만들기 위해서는 깊은 복사(deep copy)를 해야 한다.
깊은 복사를 수행하는 방법으로는 JSON.stringify()JSON.parse()를 사용하는 방법이 있다.
하지만, 이 방법은 객체 내부에 함수가 포함되어 있거나,
객체가 순환 참조(circular reference)를 가지고 있는 경우에는 사용할 수 없다.

더 나은 깊은 복사 방법으로는, lodashImmutable.js와 같은 라이브러리를 사용하거나,
직접 깊은 복사 함수를 작성하는 방법이 있다.
이를 위해서는 재귀(recursion)를 사용하여 객체 내부의 값을 순회하며,
값을 복제하여 새로운 객체를 생성하는 방법을 사용할 수 있다.

function deepCopy(obj) {
  if (typeof obj !== 'object' || obj === null) {
    return obj;
  }

  const copiedObj = Array.isArray(obj) ? [] : {};

  for (const key in obj) {
    copiedObj[key] = deepCopy(obj[key]);
  }

  return copiedObj;
}

const deepCopyObj = deepCopy(originalObj);

deepCopyObj.a.b = 3;

console.log(originalObj.a.b); // 2
console.log(deepCopyObj.a.b); // 3

참고 사이트

MDN Web Docs
JavaScript.info
Lodash
Immutable.js

profile
한 발자국, 한 걸음 느리더라도 하루하루 발전하는 삶을 살자.

0개의 댓글