얕은 복사와 깊은 복사는 데이터의 복사 방법에 대한 개념으로, 원시값과 객체를 다룰 때 각각 다르게 동작한다. 이를 쉽게 설명하면 다음과 같다:
let num1 = 1;
let num2 = num1; // num2에 num1의 값을 복사
num2 = 2;
console.log(num1, num2); // num1: 1, num2: 2
이 경우 num1과 num2는 독립적인 값을 가진다. num2를 변경해도 num1에 영향을 주지 않는다. 이는 원시값의 특성 때문이다.
const mike = {
name: 'mike',
age: 12,
};
let user = mike; // mike의 참조를 user에 복사
user.age = 14;
console.log(mike, user);
// { name: 'mike', age: 14 } { name: 'mike', age: 14 }
여기서 user는 mike와 동일한 객체를 가리킨다. 따라서 user의 값을 변경하면 mike의 값도 함께 변경된다.
얕은 복사를 수행하는 메서드와 연산자(1-depth)
- 얕은 복사를 수행하는 메서드와 연산자는 객체나 배열의 복사 시 최상위 레벨만 복사하지만, 원본과의 연결을 끊는 새로운 데이터 구조를 생성한다.
- 객체안에 객체가 있을 경우 한개의 객체라도 원본 객체를 참조하고 있다면 이를 얕은 복사라고 한다.
ㅤㅤ
예제: 얕은 복사를 수행하는 메서드const original = { name: 'mike', age: 12, details: { hobby: 'soccer' }, }; ㅤ // 얕은 복사 (최상위 레벨만 복사) const shallowCopy = { ...original }; ㅤ // 수정 shallowCopy.name = 'john'; shallowCopy.details.hobby = 'basketball'; ㅤ console.log(original); // { name: 'mike', age: 12, details: { hobby: 'basketball' } } ㅤ console.log(shallowCopy); // { name: 'john', age: 12, details: { hobby: 'basketball' } }
shallowCopy.name
은 독립적으로 복사되었으므로, 변경해도 원본에는 영향을 미치지 않는다.- 하지만 중첩된 객체인 details는 참조를 공유하므로, 내부 속성 hobby를 변경하면 원본에도 영향을 미친다.
깊은 복사는 객체나 배열을 복사할 때 중첩된 데이터까지 모두 새로운 메모리에 복사하는 방식이다.
원본과 복사본은 완전히 독립적이다.
const mike = {
name: 'mike',
age: 12,
};
const user = JSON.parse(JSON.stringify(mike)); // 깊은 복사
user.age = 14;
console.log(mike, user);
// { name: 'mike', age: 12 } { name: 'mike', age: 14 }
깊은 복사를 사용하면 user와 mike는 독립적인 객체가 되어, 한쪽을 변경해도 다른 쪽에 영향을 미치지 않는다.
JSON.stringify를 이용해 깊은 복사를 하고 데이터를 수정한 예시
// 원본 객체 const original = { name: 'Alice', age: 25, address: { city: 'New York', zip: '10001', }, }; ㅤ // JSON.stringify와 JSON.parse를 이용해 깊은 복사 const deepCopy = JSON.parse(JSON.stringify(original)); ㅤ // 복사본 데이터 수정 deepCopy.name = 'Bob'; deepCopy.address.city = 'Los Angeles'; ㅤ // 결과 확인 console.log('Original:', original); console.log('Deep Copy:', deepCopy); ㅤ // 출력 결과 // Original: { name: 'Alice', age: 25, address: { city: 'New York', zip: '10001' } } // Deep Copy: { name: 'Bob', age: 25, address: { city: 'Los Angeles', zip: '10001' } }
동작 원리
1. JSON.stringify(original)
객체를 JSON 문자열로 변환하여, 객체의 구조와 데이터를 문자열 형태로 저장한다.
2. JSON.parse()
JSON 문자열을 다시 객체로 변환한다. 이 과정에서 원본 객체의 모든 값을 복사하여 새로운 객체를 생성한다.
원시값은 깊은 복사
원시값은 데이터 크기가 작아, 복사 과정에서 독립적인 값을 생성해도 큰 비용이 들지 않는다. 따라서 원시값은 기본적으로 깊은 복사를 한다.
객체는 얕은 복사
객체는 데이터 크기가 크고, 중첩된 구조를 가질 수 있다. 만약 객체를 항상 깊은 복사한다면 성능에 큰 영향을 줄 수 있다.
비효율성의 예
const mike = {
name: 'mike',
age: 12,
address: {
city: 'Seoul',
zip: '12345',
},
};
let user = mike; // mike의 참조만 복사
만약 이 데이터를 깊은 복사로 처리했다면, 다음과 같은 새로운 객체를 생성해야 한다
let user = {
name: 'mike',
age: 12,
address: {
city: 'Seoul',
zip: '12345',
},
};
이처럼 모든 속성을 새로 복사하면 데이터 크기가 클수록 성능 부담이 크다.
JSON.stringify
또는 라이브러리를 사용해 명시적으로 처리해야 한다.