참조
자바스크립트에서 "참조(reference)"란 변수가 값이 저장된 메모리 주소를 가리키는 것을 의미한다. 자바스크립트는 변수가 원시 데이터 타입(숫자, 문자열, 불리언, null, undefined, 심볼)일 때와 참조 데이터 타입(객체, 배열, 함수 등)일 때 다르게 동작한다.
즉, 참조는 메모리의 위치를 가리키는 것을 의미한다.최상위 레벨
"최상위 레벨"은 객체의 가장 바깥쪽 레벨, 즉 객체 자체를 말한다. 객체가 다른 객체를 속성으로 가지고 있을 때, 이 속성은 최상위 레벨이 아닌 중첩된 레벨에 해당한다.
const obj = { prop1: 'value1', prop2: { nestedProp: 'value2' } };여기서 obj의 최상위 레벨은 prop1과 prop2이다. prop2는 또 다른 객체를 값으로 가지고 있지만, 이 객체의 속성들은 obj의 최상위 레벨이 아니다. prop2의 내부에 있는 nestedProp은 중첩된 레벨에 해당한다.
쉽게 생각하면 깊은복사는 참조가 서로를 공유하지 않는 복사 이다.
let num = 1;
let copyNum = num;
console.log(num); // 1
console.log(copyNum); // 1
console.log(num===copyNum); // true
copyNum = 2;
console.log(num); // 1
console.log(copyNum); // 2
console.log(num===copyNum); // false

위 코드는 어찌 보면 당연한 코드이다. 메모리 상에 각각 num과 copyNum이 생성되고 이후 copyNum에 num의 값을 복사해 넣고 copyNum의 값을 변경하면 copyNum의 값만 바뀐다.
즉, 깊은 복사는 변수의 값이 직접 복사 된다.
// 자바스크립트의 원시데이터
숫자(Number): 정수 및 부동 소수점 숫자를 나타냅니다.
let num = 42;
문자열(String): 텍스트 데이터를 나타냅니다.
let str = "Hello, World!";
불리언(Boolean): 참(true) 또는 거짓(false)을 나타냅니다.
let bool = true;
null: "값이 없음"을 나타냅니다.
let n = null;
undefined: 변수가 선언되었지만 값이 할당되지 않았음을 나타냅니다.
let u;
심볼(Symbol): 고유하고 변경 불가능한 값을 생성합니다.
let sym = Symbol("unique");
자바스크립트에서 원시 데이터 타입들은 깊은 복사 된다.
원본 객체와 같은 참조 (메모리 내의 같은 값을 가리킴)를 공유하는 복사 이다.
const num = {
one : 1,
}
const copyNum = num ;
console.log(num.one); // 1
console.log(copyNum.one); // 1
console.log(num===copyNum); // true(num과 copyNum은 서로 같은 참조 값을 갖고있다.)
copyNum.one = 2;
console.log(num.one); // 2
console.log(copyNum.one); // 2
console.log(num===copyNum); // true
분명 복사한 copyNum.one의 값을 바꿨는데 원본 객체의 값도 바뀌었다.
원시 데이터와는 다르게 객체는 얕은 복사가 일어난다.
즉, 객체(참조 데이터 타입)는 참조값이 복사 된다는 뜻이다.

객체는 값을 복사 하는게 아니라 객체가 저장되어 있는 메모리의 주소가 저장된다. 즉, 참조 값이 저장된다.
const num = {
one : 1,
}
one : 1은 메모리의 어딘가에 저장되게 되고 저장된 메모리의 참조값이 num변수에 들어감
위 처럼 객체를 선언하면 메모리의 어딘가에 해당 객체의 값이 저장되고, 그 메모리 주소의 참조값을 해당 변수에 저장하게 된다.
결국 복사한 copyNum을 변경해도 num과 같은 참조를 갖고 있기 때문에 같은 데이터를 변경하게 되는 것이다.
이런 객체의 얕은 복사에서도 원시데이터 타입을 갖는 데이터는 ...(스프레드)를 이용하면 깊은 복사가 이루어진다.
const num = {
one : 1,
}
const copyNum = {...num}; // 원시데이터는 스프레드 문법을 이용하면 깊은 복사됨.
console.log(num.one); // 1
console.log(copyNum.one); // 1
console.log(num===copyNum); // false(num과 copyNum은 서로 다른 참조 값을 같게된다)
copyNum.one = 2;
console.log(num.one); // 1
console.log(copyNum.one); // 2
console.log(num===copyNum); // false
const num = {
one : 1,
region:{
city : "서울"
}
}
const copyNum = {...num}; // 스프레드 문법을 이용했지만 또 다른 객체가 있다면?
console.log(num.region.city); // "서울"
console.log(copyNum.region.city); // "서울"
console.log(num.region===copyNum.region); // true(같은 참조값이 저장되어있음)
copyNum.region.city = "인천";
console.log(num.region.city); // "인천"
console.log(copyNum.region.city); // "인천"
console.log(num.region===copyNum.region); // true
분명 스프레드 문법을 이용해 객체를 복사했지만 copyNum.region.city값을 변경했더니 원본 객체의 데이터도 바뀌었다.
즉, 스프레드 문법을 이용해 복사 하더라도 객체라면 얕은 복사가 이뤄진다.
객체 내부의 객체까지 깊은 복사 하고싶다면 내부의 객체도 스프레드 문법을 이용해 주어야한다.
const num = {
one : 1,
region:{
city : "서울"
}
}
const copyNum = {
...num,
region : {
...num.region
}
};
console.log(num.region.city); // "서울"
console.log(copyNum.region.city); // "서울"
console.log(num.region===copyNum.region); // false(서로 다른 참조값이 저장됨)
copyNum.region.city = "인천";
console.log(num.region.city); // "서울"
console.log(copyNum.region.city); // "인천"
console.log(num.region===copyNum.region); // false
요약
- 깊은 복사: 원시 데이터는 깊은 복사가 이루어지며
값이 복사 된다.- 얕은 복사: 객체는 얕은 복사되며 객체가 저장되어 있는
메모리 주소의 참조 값이 복사 된다.- 참조 데이터 타입(객체)의 경우, 최상위 레벨에서는 복사가 이루어지지만, 중첩된 객체나 배열은 동일한 참조를 공유하는 얕은 복사가 이뤄진다.
- 깊은 복사를 하고싶다면
재귀적 복사,JSON을 이용한 복사,라이브러리(lodash)를 이용하자- 변수에 객체를 저장 한다는 것은 값을 저장 하는게 아닌 객체가 저장되어 있는 메모리의 참조값이 저장한다는 뜻이다.