주요 개념
- 원시 타입(Primitive Type)
- 참조 타입(Reference Type)
- 깊은 복사(Deep copy)
- 얕은 복사(Shallow copy)
JS에는 원시 타입(Primitive Type)과 참조 타입(Reference Type)이 있다.
1. 원시값
- Number
- String
- Boolean
- Null
- Undefined
2. 객체(참조)값
- Object
- Symbol
원시 타입인 숫자, 문자열, 논리형, 널, 언디파인드 다섯 가지를 제외한 모든 값은 객체(참조) 타입이다. 참조 타입이라고 불리는 이유는 객체의 모든 연산이 실제 값이 아닌 참조값으로 처리되기 때문이다.
이름에서 알 수 있든 원시 타입은 값 자체를 복사하여 사용한다. 그렇기 때문에 어떠한 변수를 복사하고 나서 원본에 해당하는 변수를 변경시켜도 복사된 변수의 값에 영향을 끼치지 않는다. 아래는 예제이다.
var temp1 = 100;
var temp2 = num1;
console.log(temp2); // 100
num1 = 200;
console.log(temp2); // 100
참조 타입은 주소 값을 참조하여 사용한다. 따라서 원본 데이터가 바뀌면 복사한 데이터도 변경되게 된다.
var obj1 = {
name: 'alien'
}
var obj2 = obj1;
console.log(obj2.name); // alien
obj1.name = 'thor';
console.log(obj2.name); // thor
이런 특성 때문에 복사하는 방식도 다르다.
얕은 복사(Shallow Copy)란 객체를 복사할 때 원본 값과 복사된 값이 같은 참조를 가리키고 있는 것이다. 객체 안에 객체가 있을 경우 한 개의 객체라도 원본 객체를 참조하고 있다면 이를 얕은 복사라고 한다. 얕은 복사를 해놓고 그 변수를 재사용하여 갱신시키면 당연히 원본 값이 동시에 변하므로 의도하지 않은 상황이 발생할 수 있다.
얕은 복사의 첫 번째 방법은 그냥 일반적인 복사와 동일하다.
1. 일반 복사
const obj1 = { name: 'alien' }
const obj2 = obj;
newObj.vaule = 'bat man';
console.log(obj.name); // 'bat man'
console.log(obj === obj2); // true
2. Object.assign()
Object.assign()을 이용하면 객체 자체는 깊은 복사가 수행된다. 메소드는 Object.assign(생성할 객체, 복사할 객체)와 같은 형식으로 사용한다. 하지만 이 방법은 2차원 이상 객체에 대해서는 깊은 복사가 이루어지지 않는다. 아래 코드를 보면 객체는 서로 다른 곳을 참조하고 있어 깊은 복사가 이루어졌지만 내부의 객체는 같은 곳을 참조하고 있다는 것을 알 수 있다.
const obj = {
a: 1,
b: {
c: 2,
},
};
const obj2 = Object.assign({}, obj);
obj2.b.c = 3
obj === obj2 // false
obj.b.c === obj2.b.c // true
추가적으로 해당 방식은 아래와 같이 사용할 수 있다.
const A = {a: 1, b: 2};
const B = {c: 1, d: 2};
const result = Object.assign(A, B);
console.log(result); // { a: 1, b: 2, c: 3, d: 4 }
3. 전개 구문
전개 구문도 Object.assign()과 마찬가지로 복사한 객체 자체는 깊은 복사이지만 내부의 객체는 얕은 복사가 진행된다.
const obj = {
a: 1,
b: {
c: 2,
},
};
const obj2 = {...obj}
copiedObj.b.c = 3
obj === obj2 // false
obj.b.c === obj2.b.c // true
깊은 복사(Deep Copy)는 객체가 다른 주소를 참조하고 있으며 안의 값들만 그대로 복사되는 복사이다. 참조 타입의 객체를 얕은 복사 하여 사용하게 되면 코드를 작성하다 나중에 해당 객체의 특정 객체 값을 수정했다가 문제가 발생하고 원인을 찾기 힘들어질 수도 있으므로 상황에 따라 얕은 복사와 깊은 복사를 잘 이용해주어야 한다.
아래는 깊은 복사 방법이다.
1. 재귀 함수
const obj = {
a: 1,
b: {
c: 2,
},
};
function copyObj(obj) {
const res = {};
for (let key in obj) {
if (typeof obj[key] === 'object') {
res[key] = copyObj(obj[key]);
} else {
res[key] = obj[key];
}
}
return res;
}
const obj2 = copyObj(obj);
obj2.b.c = 3
obj.b.c === obj2.b.c //false
2. JSON 객체 이용
const obj = {
a: 1,
b: {
c: 2,
},
};
const obj2 = JSON.parse(JSON.stringify(obj));
obj2.b.c = 3
obj.b.c === obj2.b.c //false
3. lodash 라이브러리 cloneDeep() 메소드 사용
사용 이전에 npm install lodash를 수행하여 해당 패키지를 설치해주어야 한다.
const lodash = require("lodash");
const obj = {
a: 1,
b: {
c: 2,
},
func: function () {
return this.a;
},
};
const obj2 = lodash.cloneDeep(obj);
newObj.b.c = 3;
console.log(obj); // { a: 1, b: { c: 2 }, func: [Function: func] }
console.log(obj.b.c === obj2.b.c); // false
방식은 다르지만 Python에서도 얕은 복사와 깊은 복사가 존재한다. Python에는 모든 것이 객체이므로 더 신경을 써줘야 할 때가 있다.