데이터 불변성(immutability)과 얕은복사(shallow copy), 깊은복사(deep copy)

HeeGeun.Lee·2022년 11월 14일
1

javascript

목록 보기
5/5
post-thumbnail

Introduction

immutability(불변성)은 무엇이고 그에 따른 복사방법(shallow copy, deep copy)에 대해 알아보자.

데이터 불변성(immutability)

자바스크립트에서 불변성이란 객체가 생성된 이후 그 상태를 변경할 수 없는 것을 의미한다.
여기서 상태를 변경할 수 있는 것과 값을 재할당하는 것은 다르다는 것을 알아야 한다.

1. 원시 데이터: String, Number, Boolean, undefined, null

let a = 1;
let b = 4;
console.log(a, b, a === b); // 1 4 false


변수ab는 서로 다른 메모리 주소를 바라보고 있고, 그 둘은 다르다는 것을 알 수 있다.

b = a;
console.log(a, b, a === b); // 1 1 true


변수a가 바라보는 메모리 주소를 b에 할당하면, ab는 같은 메모리 주소를 바라보게 되므로 a === btrue가 나오게 된다.

let c = 1;
console.log(a, b, c, a === c); // 1 1 1 true


변수c에는 기존의 메모리에 1이 있으므로 새로운 메모리 주소를 할당받지 않고 ab가 바라보는 메모리 주소를 똑같이 바라보게 된다.

이렇게 원시데이터는 바라보는 메모리 주소가 변하고, 기존의 메모리는 변하지 않는다.
이것을 데이터의 불변성(immutability) 이라고 한다.

2. 참조형 데이터: Object, Array, Function

그에 반해 참조형 데이터는 가변 한다.

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


변수ab를 같은 메모리 주소를 바라보게한 상태에서

a.k = 7;
console.log(a, b, a === b); // {k: 7} {k: 7} true

변수a의 k값만 변경했지만, 변수b의 값도 같이 변경되었다는 것을 알 수 있는데 이것은 "복사"된 것이 아닌 메모리 주소만 변경했기 때문에 일어난 일이다.
의도했다면 다행이지만 그렇지 않았다면 이것은 큰 문제가 발생할 것이다.

그렇다면 참조형 데이터의 불변성을 지키면서 "복사" 할 수 있는 방법은 뭘까?

얕은복사(shallow copy)

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

그럼 껍데기와 내용물 모두 깊게(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()메서드를 사용하면 위처럼 완벽하게 깊은복사를 할 수 있다.

결론

자바스크립트의 중요한 특징 중 하나인 불변성에 대해 알아보았고, 그 특징을 이용한 배열, 객체 등의 참조형 데이터의 복사 방법에 대해 정리했다.
어려운 개념이었지만, 차근차근 정리를 하다 보니 알 거 같기도 하다.

profile
느리지만 천천히.

0개의 댓글