
🌷 얕은 복사와 깊은 복사
최근들어 내가 기본이 아직 많이 부족하다는 것을 정말 많이 느낀다. 짧은 시간 공부하고 취업해서 개발자라고 이야기를 하고 있으니.. 그럴 수 밖에. 아직 어디를 가면 스스로를 개발자라고 소개하기가 부끄러울 때가 많다. 그래서 기본이 부족했던 부분들을 다시 열심히 공부해서 차곡차곡 쌓아야겠다는 생각이들었다.
오늘은 얕은 복사와 깊은 복사에 대해 조금 더 깊게 확인하고 제대로 이해를 해봐야겠다고 생각하고 정리를 해봤다. 지금 회사 입사를 할 때 받았던 질문이었는데 그 때는 정말 두루뭉술하게만 대답하고 이해했던 것 같아서, 이참에 제대로 알고 가보자!
🖐🏻 얕은 복사와 깊은 복사를 알아보기 전에,
📝 데이터 타입
원시형
참조형
👉🏻 자세히 알아보기
📝 데이터 타입에 따른 메모리 동작
- 원시형
// 변수 poke 에 문자열 "피카츄"를 할당
var poke = "피카츄";
/* 메모리 동작
1. &17 의 값에 undefined 할당
2. 식별자 poke 에 &17 할당
3. 새로운 공간인 &27 에 "피카츄"을 저장
4. poke 에 &27할당
*/
* & : 메모리 주소를 의미 ex. &27 는 메모리의 27번 주소

- 참조형 - property 하나를 가질 경우
// 변수 startingPokemon 에 객체를 할당
var starting = {
name : "이상해씨"
};
/* 메모리 동작
1. &18의 값에 undefined 를 저장
2. 식별자 starting 에 &28 을 할당 "주소의 이름이 됨!"
3. 새로운 공간인 &28 에 객체를 저장할 공간의 주소인 &37 를 할당
- 즉, &28 번 주소의 값이 &37 이 됨
- 원시형 데이터는 &27번 주소의 값이 "피카츄"
4. &12(name) 에 "이상해씨" 라는 문자열 값을 저장
5. starting 에 &28 을 할당
* 결과
- starting 변수의 값은 &18
- &18 에는 객체 프로퍼티를 저장하고 있는 저장 공간의 주솟값(&37)를 가짐
- 객체는 주솟값을 이용해 참조
*/

- 참조형 - 2가지 이상의 프로퍼티를 가질 경우
// 변수 startingPokemon 에 객체를 할당
var myPoke = {
first : "피카츄"
second : "이상해씨"
};

🙋🏻♀️ 복사의 종류
deep copy : A deep copy means that all of the values of the new variable are copied and disconnected from the original variable.
shallow copy : A shallow copy means that certain (sub-)values are still connected to the original variable.
얕은 복사
참조 복사 (reference copy)
객체 복사 (object copy)
ex )
Array.prototype.slice(),Object.assign(), Spread 연산자 (전개 연산자)
깊은 복사
깊은 복사 (deep copy)
ex)
JSON.parse(),JSON.stringify, 재귀 함수를 구현한 복사,Lodash라이브러리 사용
/* 1. 중첩된 객체 예시 */
var startingPokemon = {
generation: '1세대',
pokemon : {
name : "이상해씨",
number : 1
}
};
# 참조 복사
var referenceCopy = startingPokemon;
# 객체 복사
var shallowCopy = shallowCopy(startingPokemon);
const shallowCopy = ( targetObj ) => {
var returnObj = {};
// targetObj 를 순회하며
// key-targetObj[key] (key-value) 쌍으로 returnObj 추가
for (var key in targetObj) {
returnObj[key] = targetObj[key];
}
return returnObj; // 새로 만들어진 객체를 반환
};
# 깊은 복사
var deepCopy = deepCopy(startingPokemon);
const deepCopy = ( targetObj ) => {
var returnValue = {};
if (typeof targetObj === 'object' && targetObj !== null) {
// targetObj 가 객체라면 프로퍼티를 재귀 형식으로 호출
for (var key in targetObj) {
returnValue[key] = deepCopy(targetObj[key]);
}
} else {
// target 이 객체가 아니라면 값 반환
returnValue = target;
}
return returnObj;
}
💁🏻♀️ 복사 결과
1️⃣ 얕은 복사 - 참조 복사 (주솟값 복사)
# 참조 복사 // referenceCopy
var starting = {
generation: '1세대',
pokemon : {
name : "이상해씨",
number : 1
}
};
var rfc = starting;
복사 결과 확인
# 원본객체
&11 (shallowCopy)
- &21
- &31 (generation)
- &32 (pokemon)
- &24 (name) : "이상해씨"
- &25 (number) : 1
# 참조 복사
&12 (shallowCopy)
- &21
- &31 (generation)
- &32 (pokemon)
- &24 (name) : "이상해씨"
- &25 (number) : 1

2️⃣ 얕은 복사 - 객체 복사
// 객체 복사 - shallowCopy
var starting = {
generation: '1세대',
pokemon : {
name : "이상해씨",
number : 1
}
};
var sc = shallowCopy(starting);
const shallowCopy = ( targetObj ) => {
var returnObj = {};
// targetObj 를 순회하며
// key-targetObj[key] (key-value) 쌍으로 returnObj 추가
for (var key in targetObj) {
returnObj[key] = targetObj[key];
/*
returnObj['generation'] = targetObj['generation'];
returnObj['pokemon'] = targetObj['pokemon']; // 객체를 가져옴
*/
}
return returnObj; // 새로 만들어진 객체를 반환
};
- 복사 결과 확인
# 원본객체
&11 (shallowCopy)
- &21
- &31 (generation)
- &32 (pokemon)
- &24 (name) : "이상해씨"
- &25 (number) : 1
# 객체 복사
&13 (shallowCopy)
- &26
- &51 (generation)
- &32 (pokemon)
- &24 (name) : "이상해씨"
- &25 (number) : 1

3️⃣ 깊은 복사
# 깊은 복사
var starting = {
generation: '1세대',
pokemon : {
name : "이상해씨",
number : 1
}
};
var deepCopy = deepCopy(starting);
const deepCopy = ( targetObj ) => {
var returnValue = {};
if (typeof targetObj === 'object' && targetObj !== null) {
// targetObj 가 객체라면 프로퍼티를 재귀 형식으로 호출
for (var key in targetObj) {
returnValue[key] = deepCopy(targetObj[key]);
}
} else {
// target 이 객체가 아니라면 값 반환
returnValue = target;
}
return returnObj;
}
복사 결과
- 모든 주소의 값이 달라짐
# 원본객체
&11 (shallowCopy)
- &21
- &31 (generation)
- &32 (pokemon)
- &24 (name) : "이상해씨"
- &25 (number) : 1
# 깊은 복사
&14 (dc)
- &27
- &61 (generation)
- &62 (pokemon)
- &71 (name) : "이상해씨"
- &72 (number) : 1

👩🏻💻 각 객체들의 주솟값 비교 모아보기
🔗 : 원본 객체와 연결
🆕 : 원본 객체가 아닌 새로운 값
# 원본객체
&11 (shallowCopy)
- &21
- &31 (generation)
- &32 (pokemon)
- &24 (name) : "이상해씨"
- &25 (number) : 1
# 참조 복사
&12 (shallowCopy)
- &21 🔗
- &31 (generation)
- &32 (pokemon)
- &24 (name) : "이상해씨"
- &25 (number) : 1
# 깊은 복사
&14 (dc)
- &27 🆕
- &61 (generation) 🆕
- &62 (pokemon) 🆕
- &71 (name) : "이상해씨"
- &72 (number) : 1
# 얕은 복사
&13 (shallowCopy)
- &26 🆕
- &51 (generation) 🆕
- &32 (pokemon) 🔗
- &24 (name) : "이상해씨"
- &25 (number) : 1
🌝 결론
복사한 객체가 원래 객체에 연결이 되어있다면 (주솟값을 참고) 얕은복사
복사한 객체가 원래 객체에 연결되어 있지 않다면 깊은복사
느낀 점
- 직접 데이터 주소 구조를 그려가며 이해해보니 확 머리에 들어왔던… 어렵다고 이해할 수 없는 것들은 없는 것 같다. 열심히 파다보면 역시 내 것이 되는 ㅠ_ㅠ 열심히 하자..!
혹시 내용 중 틀린 점이 있다면 알려주세요! 응애 개발자의 성장을 위해..!
![[월간 이트루] 3월의 회고.](https://cdn.hashnode.com/res/hashnode/image/upload/v1711895194426/f6a22b40-ffd3-4b11-a25b-e1b040b11853.png)
![[월간 이트루] 2월의 회고.](https://cdn.hashnode.com/res/hashnode/image/upload/v1709129901980/5a2af3c5-8f6e-4f07-9c4f-1b43bef27ae9.png)


![[월간 이트루] 1월의 회고.](https://cdn.hashnode.com/res/hashnode/image/upload/v1706708547437/51af577d-21f9-4256-8939-95c577a58abc.jpeg)