데이터 불변성이란 데이터가 생성된 이후 변경되지 않는 것을 의미합니다.
불변성을 유지하면 코드의 안정성, 가독성, 유지보수성, 성능 등을 개선할 수 있습니다.
JavaScript에서 객체와 배열은 참조 자료형이므로 특히 불변성을 유지하는 것이 중요합니다. 객체나 배열이 변경될 때 새로운 객체나 배열을 생성하는 것이 일반적이기 때문입니다. 이를 통해 데이터 변경으로 인한 예상치 못한 오류를 줄일 수 있습니다.
얕은 복사는 원본 객체의 프로퍼티들을 참조하는 새로운 객체를 생성하는 방법입니다. 얕은 복사를 사용하면 원본 객체의 프로퍼티 중 하나가 변경되면 복사된 객체의 프로퍼티도 함께 변경됩니다.
이는 데이터 불변성을 지키지 않을 수 있습니다.
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = Object.assign({}, obj1);
console.log(obj1.a === obj2.a) // false
console.log(obj1.b === obj2.b) // true
obj1.a = 10;
obj1.b.c = 20;
console.log(obj1); // { a: 10, b: { c: 20 } }
console.log(obj2); // { a: 1, b: { c: 20 } }
위 코드에서 Object.assign() 함수를 사용하여 obj1 객체를 복사하여 obj2 객체를 생성합니다. 그리고 obj1 객체의 a 프로퍼티를 변경하고, obj1 객체의 b.c 프로퍼티를 변경합니다. 그 결과, obj2 객체의 b.c 프로퍼티도 함께 변경됩니다. 이는 얕은 복사를 사용했기 때문입니다.
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = { ...obj1 };
console.log(obj1.a === obj2.a) // false
console.log(obj1.b === obj2.b) // true
obj1.a = 10;
obj1.b.c = 20;
console.log(obj1); // { a: 10, b: { c: 20 } }
console.log(obj2); // { a: 1, b: { c: 20 } }
깊은 복사는 원본 객체의 모든 프로퍼티들을 복사하여 완전히 새로운 객체를 생성하는 방법입니다. 깊은 복사를 사용하면 복사된 객체와 원본 객체는 서로 독립적이기 때문에 원본 객체의 프로퍼티가 변경되어도 복사된 객체의 프로퍼티는 변경되지 않습니다. 이는 데이터 불변성을 지키는 방법 중 하나입니다. JavaScript에서는 Lodash 라이브러리를 사용하여 깊은 복사를 수행할 수 있습니다.
const _ = require('lodash');
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = _.cloneDeep(obj1);
console.log(obj1.a === obj2.a) // false
console.log(obj1.b === obj2.b) // false
obj1.a = 10;
obj1.b.c = 20;
console.log(obj1); // { a: 10, b: { c: 20 } }
console.log(obj2); // { a: 1, b: { c: 2 } }
위 코드에서 _.cloneDeep() 함수를 사용하여 obj1 객체를 복사하여 obj2 객체를 생성합니다. 그리고 obj1 객체의 a 프로퍼티를 변경하고, obj1 객체의 b.c 프로퍼티를 변경합니다.
Lodash 라이브러리의 _.cloneDeep() 함수는 객체나 배열의 모든 하위 객체나 배열까지 재귀적으로 복사합니다. 이는 매우 효과적이지만, 대규모의 데이터를 다룰 때는 성능 문제가 발생할 수 있습니다.
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = JSON.parse(JSON.stringify(obj1));
console.log(obj1.a === obj2.a) // false
console.log(obj1.b === obj2.b) // false
obj1.a = 10;
obj1.b.c = 20;
console.log(obj1); // { a: 10, b: { c: 20 } }
console.log(obj2); // { a: 1, b: { c: 2 } }
위 코드에서 JSON.stringify() 함수를 사용하여 obj1 객체를 JSON 문자열로 변환하고, JSON.parse() 함수를 사용하여 JSON 문자열을 다시 객체로 변환하여 obj2 객체를 생성합니다. 이를 통해 obj1 객체의 모든 프로퍼티를 복사하여 새로운 객체를 생성합니다. 그리고 obj1 객체의 a 프로퍼티를 변경하고, obj1 객체의 b.c 프로퍼티를 변경합니다. 그러나, JSON.parse()와 JSON.stringify()에는 깊은 복사가 되지 않는 예외가 있습니다.
객체나 배열의 크기가 클 경우에는 복사 시간이 오래 걸리거나 성능 문제가 발생할 수 있습니다.
JavaScript에서 객체나 배열을 다룰 때는 얕은 복사와 깊은 복사를 적절히 사용해야 합니다. 객체나 배열의 크기가 작거나 데이터 변경이 적을 때는 얕은 복사를 사용하는 것이 적절합니다. 하지만, 객체나 배열의 크기가 클 경우나 데이터 변경이 많을 경우에는 깊은 복사를 사용하는 것이 더 적절합니다.
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Spread_syntax
이 블로그는 코드스테이츠 자료와 chatGPT의 답변들로 만들었습니다.