immutability(불변성)은 무엇이고 그에 따른 복사방법(shallow copy, deep copy)에 대해 알아보자.
자바스크립트에서 불변성이란 객체가 생성된 이후 그 상태를 변경할 수 없는 것을 의미한다.
여기서 상태를 변경할 수 있는 것과 값을 재할당하는 것은 다르다는 것을 알아야 한다.
let a = 1;
let b = 4;
console.log(a, b, a === b); // 1 4 false
변수a
와 b
는 서로 다른 메모리 주소를 바라보고 있고, 그 둘은 다르다는 것을 알 수 있다.
b = a;
console.log(a, b, a === b); // 1 1 true
변수a
가 바라보는 메모리 주소를 b
에 할당하면, a
와 b
는 같은 메모리 주소를 바라보게 되므로 a === b
는 true
가 나오게 된다.
let c = 1;
console.log(a, b, c, a === c); // 1 1 1 true
변수c
에는 기존의 메모리에 1이 있으므로 새로운 메모리 주소를 할당받지 않고 a
와 b
가 바라보는 메모리 주소를 똑같이 바라보게 된다.
이렇게 원시데이터는 바라보는 메모리 주소가 변하고, 기존의 메모리는 변하지 않는다.
이것을 데이터의 불변성(immutability) 이라고 한다.
그에 반해 참조형 데이터는 가변 한다.
let a = { k: 1 };
let b = { k: 1 };
console.log(a, b, a === b); // {k: 1} {k: 1} false
참조형 데이터는 위처럼 값이 같을지라도 다른 메모리 주소를 바라보고 있으므로 다른 값이다.
b = a;
console.log(a, b, a === b); // {k: 1} {k: 1} true
변수a
와 b
를 같은 메모리 주소를 바라보게한 상태에서
a.k = 7;
console.log(a, b, a === b); // {k: 7} {k: 7} true
변수a
의 k값만 변경했지만, 변수b
의 값도 같이 변경되었다는 것을 알 수 있는데 이것은 "복사"된 것이 아닌 메모리 주소만 변경했기 때문에 일어난 일이다.
의도했다면 다행이지만 그렇지 않았다면 이것은 큰 문제가 발생할 것이다.
그렇다면 참조형 데이터의 불변성을 지키면서 "복사" 할 수 있는 방법은 뭘까?
const user = {
name: 'John Doe',
age: 40,
emails: ['john@gmail.com'],
};
// const copyUser = user; (X)
// const copyUser = Object.assign({}, user); // (O)
const copyUser = {...user}; (O)
console.log(copyUser === user); // false
user.age = 20;
console.log(user);
// {name: 'John Doe', age: 20, emails: Array(1)}
console.log(copyUser);
// {name: 'John Doe', age: 40, emails: Array(1)}
참조형 데이터는 위와 같이 Object.assign()
을 이용하거나 {...data}
spread operator를 이용해서 복사하면 불변성을 유지하면서 복사할 수 있다.
하지만 이방법은 문제가 하나 있다.
user.emails.push('doe@naver.com');
console.log(user.emails === copyUser.emails); // true
console.log(user);
// {name: 'John Doe', age: 20, emails: ['john@gmail.com', 'doe@naver.com']}
console.log(copyUser);
// {name: 'John Doe', age: 40, emails: ['john@gmail.com', 'doe@naver.com']}
user
객체의 emails배열에 데이터를 추가했더니 copyUser
의 emails배열에도 데이터가 추가되었다.
이렇게된 이유는 배열도 참조형 데이터인데 객체의 껍데기만 복사를 하고 그안의 배열은 복사를 하지 않았기 때문이다.
그럼 껍데기와 내용물 모두 깊게(deep) 복사를 해볼텐데 깊은복사를 하려면 객체와 자바스크립트 내장함수를 재귀적으로 사용해야 하는데 이걸 직접 구현하려면 복잡하니, lodash를 이용해보자.
import _ from 'lodash';
const user = {
name: 'John Doe',
age: 40,
emails: ['john@gmail.com'],
};
const copyUser = _.cloneDeep(user);
user.age = 20;
user.emails.push('doe@naver.com');
console.log(user);
// {name: 'John Doe', age: 20, emails: ['john@gmail.com', 'doe@naver.com']}
console.log(copyUser);
// {name: 'John Doe', age: 40, emails: ['john@gmail.com']}
lodash의 cloneDeep()
메서드를 사용하면 위처럼 완벽하게 깊은복사를 할 수 있다.
자바스크립트의 중요한 특징 중 하나인 불변성에 대해 알아보았고, 그 특징을 이용한 배열, 객체 등의 참조형 데이터의 복사 방법에 대해 정리했다.
어려운 개념이었지만, 차근차근 정리를 하다 보니 알 거 같기도 하다.