
JS에서 숫자, 문자, 불린, 심볼, null, undefined 6개는 원시 타입 이고, 그 외에는 객체 타입 으로 분류한다.
원시 타입과 객체 타입의 차이점은 무엇일까?
바로 원시 타입은 immutable(변경 불가능) 한 값이고, 객체 타입은 mutable(변경 가능) 한 값이라는 것이다.
이런 차이점을 이해하기 위해서는 원시 타입과 객체 타입의 값이 메모리에 어떻게 저장되고 접근할 수 있는지 알아야 한다.
원시 값을 가지는 원본 변수가 있다. 그리고 이 원본 변수를 새로운 변수에 할당했을 때 둘 중 하나의 변수를 다른 값으로 재할당하면 두 변수의 값은 동일할까?
정답은 No 이다.
var original = 1; // 원본 변수 선언 및 할당
var copy = original; // 원본 변수를 copy 에 할당
console.log(original === copy); // true; 1로 값이 같음
original = 10; // 원본 변수 재할당
console.log(original, copy); // 10 1
원본 변수에 있는 값을 copy 변수에 할당했을 때, copy 는 새로운 메모리 주소를 가리키고 있고 그 메모리에 저장되어 있는 것은 바로 원본 변수에게 받은 원시 값이다.
즉, JS에서 값에 대한 전달(call by value) 은 원시 값 그대로를 전달해주며 값을 변경하는 것은 불가능하다. 다만 재할당을 통해서만 값을 변경할 수 있다.
재할당을 했을 때, 원본 변수는 또다른 새 주소를 가리키기 때문에 원본 데이터의 값 자체가 바뀌는 것이 아니다. 그래서 원시 값은 안정성과 신뢰성을 확보할 수 있다.

반면 원본 변수에 객체 타입을 할당했을 경우는 어떻게 될까?
새로운 변수에 원본 객체 값을 할당해주고 원본 객체 값을 변경하면 새로운 변수와 원본 객체는 같을까?
기본적으로는 두 변수는 같은 메모리를 주소를 가리키기 때문에 같다. 하지만 원시 값처럼 깊은 복사(Deep Copy)를 하면 얘기가 달라진다.
객체는 원시 값처럼 깊은 복사가 이루어지지 않고 왜 얕은 복사를 하는 것일까?
그 이유는 메모리의 성능과 비용에 있다.
원시 타입의 경우 문자열은 한 문자당 2바이트씩, 숫자는 8바이트씩 메모리를 차지하도록 ECMAScript에서 정한 기준이 있다. 다른 원시 타입은 브라우저 제조사마다 조금씩 다르지만 객체 타입에 비해 적은 메모리를 사용하는 경우가 많다.
객체 타입은 메모리를 많이 사용하기 때문에 효율적으로 메모리를 관리하기 위해서는 값에 대한 전달 방법이 아닌 참조에 대한 전달 방법을 사용한다.
즉, 얕은 복사를 하며 이러한 방법은 값의 안정성과 신뢰성을 잃게 되는 리스크도 존재한다.
다시 질문으로 돌아가서 새로운 객체는 원본 객체와 같은 값을 공유 하게 되어 둘 중 한 변수의 프로퍼티를 변경하거나 삭제하면 원본 변수도 같은 영향을 받는다.
var original = {
a: 1,
b: 2
};
var copy = original;
console.log(copy); // { a: 1, b: 2 }
copy.a = 'boo'; // 복사본 변수 프로퍼티 a 값 변경
console.log(original); // { a: 'boo', b: 2 }
console.log(copy); // { a: 'boo', b: 2 }
console.log(original === copy); // true
분명 copy 변수의 프로퍼티를 변경했는데 원본 변수의 값도 변경되어 있고 original === copy 도 참의 결과가 나왔다.
그 이유는 객체는 참조에 대한 전달 방법이기 때문에 두 변수는 객체 데이터를 저장하고 있는 메모리 주소를 공유하게 된 것이다.

객체는 원시 값과 다르게 값을 변경할 수 있다. 그래서 기존 객체의 프로퍼티 a의 값도 변경이 될 수 있었고, 프로퍼티 삭제, 프로퍼티 추가도 모두 가능하다.
만약 원본 객체의 값을 안전한게 보장하면서 새로운 객체를 만들고 싶다면 어떻게 할 수 있을까? 바로 깊은 복사를 하는 것이다.
객체 리터럴 {} 을 사용해 새로운 객체를 생성했던 것처럼 {} 와 ... 스프레드 연산자를 사용해보자.
var original = { a: 1 };
var copy = { ...original }; // original을 깊은복사
console.log(copy); // { a: 1 }
copy.a = 'boo'; // copy의 프로퍼티 a값 변경
console.log(original); // { a: 1 }
console.log(copy); // { a: 'boo' }
위의 코드를 보면 깊은 복사를 통해 원본 객체 original 의 값은 그대로인데 copy 의 프로퍼티 a의 값만 변경된 것을 확인할 수 있다.
객체 리터럴로 각각 정의를 하면서 두 변수가 가리키는 참조 값이 다르기 때문이다.
하지만 객체 안에 객체 구조로 복잡하게 중첩되어 있으면 어떻게 될까?
그렇다면 위의 방법으로는 깊은 복사를 하기 어려워지게 된다.
대신 lodash 라는 라이브러리를 사용해 중첩 구조의 객체를 쉽게 깊은 복사할 수 있다.
// npm install lodash 가정
var lodash = require('lodash');
var original = { a: { b: 1 } };
var copy = lodash.cloneDeep(original);
// original과 copy가 가리키는 참조 값이 다름
console.log(original === copy); // false
// 각각의 a가 가리키는 참조 값이 다름
console.log(original.a === copy.a); // false