////
Search
👾

얕은 복사와 깊은 복사

참조형 데이터를 복사하는 경우의 문제점

let obj1 = {c: 10, d: 'ddd'}; let obj2 = obj1;
JavaScript
복사
변수 영역
주소
1001
1002
1003
1004
데이터
이름: obj1 값: @3001
이름: obj2 값: @3001
데이터 영역
주소
3001
3002
3003
3004
데이터
@5001~5002
10
‘ddd’
프로퍼티 변수 영역
주소
5001
5002
5003
5004
데이터
이름: c 값: @3002
이름: d 값: @3003
obj2.c = 20;
JavaScript
복사
변수 영역
주소
1001
1002
1003
1004
데이터
이름: obj1 값: @3001
이름: obj2 값: @3001
데이터 영역
주소
3001
3002
3003
3004
데이터
@5001~5002
10
‘ddd’
20
프로퍼티 변수 영역
주소
5001
5002
5003
5004
데이터
이름: c 값: @3004
이름: d 값: @3003
console.log(obj1.c, obj2.c);
JavaScript
복사
→ 서로의 프로퍼티 값이 다르게 나올 것 같았지만 실제론 같은 20이 출력
→ 원본이나 사본의 프로퍼티 값을 변경하면 둘 다 영향을 받음.

얕은 복사

바로 아래 단계의 값만 복사하는 방법
기존 정보를 복사해서 새로운 객체를 반환하는 함수(얕은 복사)
중첩된 객체에 대한 얕은 복사

얕은 복사 메모리 영역에서 확인해보기

let user2 = copyObject(user); // user2에 user에 대한 얕은 복사 실행됨
변수 영역
주소
1001
1002
1003
1004
데이터
이름: user 값: @3001
이름: user2 값: @3006
데이터 영역
주소
3001
3002
3003
3004
3005
3006
3007
데이터
@5001~5002
‘lion’
‘lv 1’
'lv 2’
‘lv 3’
@5003~5004
‘jisu’
프로퍼티 변수 영역
주소
5001
5002
5003
5004
데이터
이름: name 값: @3002
이름: skills 값: @7001~7003
이름: name 값: @3002
이름: skills 값: @7001~7003
skills 객체의 변수 영역
주소
7001
7002
7003
7004
데이터
이름: ‘HTML’ 값: @3003
이름: ‘CSS’ 값: @3004
이름: ‘JS’ 값: @3005
user2.name = 'jisu'; 실행
변수 영역
주소
1001
1002
1003
1004
데이터
이름: user 값: @3001
이름: user2 값: @3006
데이터 영역
주소
3001
3002
3003
3004
3005
3006
3007
데이터
@5001~5002
‘lion’
‘lv 1’
'lv 2’
‘lv 3’
@5003~5004
‘jisu’
프로퍼티 변수 영역
주소
5001
5002
5003
5004
데이터
이름: name 값: @3002
이름: skills 값: @7001~7003
이름: name 값: @3007
이름: skills 값: @7001~7003
skills 객체의 변수 영역
주소
7001
7002
7003
7004
데이터
이름: ‘HTML’ 값: @3003
이름: ‘CSS’ 값: @3004
이름: ‘JS’ 값: @3005
→ name은 user 객체에 직접 속한 프로퍼티로, user2가 값을 수정해도 새로운 데이터를 만들기 때문에 서로 다른 값임.
user.skills.HTML = 'lv 5'; // 중첩된 객체의 프로퍼티 값을 수정함
변수 영역
주소
1001
1002
1003
1004
데이터
이름: user 값: @3001
이름: user2 값: @3006
데이터 영역
주소
3001
3002
3003
3004
3005
3006
3007
3008
데이터
@5001~5002
‘lion’
‘lv 1’
'lv 2’
‘lv 3’
@5003~5004
‘jisu’
‘lv 5’
프로퍼티 변수 영역
주소
5001
5002
5003
5004
데이터
이름: name 값: @3002
이름: skills 값: @7001~7003
이름: name 값: @3007
이름: skills 값: @7001~7003
skills 객체의 변수 영역
주소
7001
7002
7003
7004
데이터
이름: ‘HTML’ 값: @3008
이름: ‘CSS’ 값: @3004
이름: ‘JS’ 값: @3005
⇒ 둘이 같은 값을 출력하고 있음
user2.skills.CSS = ‘lv 3’
변수 영역
주소
1001
1002
1003
1004
데이터
이름: user 값: @3001
이름: user2 값: @3006
데이터 영역
주소
3001
3002
3003
3004
3005
3006
3007
3008
데이터
@5001~5002
‘lion’
‘lv 1’
'lv 2’
‘lv 3’
@5003~5004
‘jisu’
‘lv 5’
프로퍼티 변수 영역
주소
5001
5002
5003
5004
데이터
이름: name 값: @3002
이름: skills 값: @7001~7003
이름: name 값: @3007
이름: skills 값: @7001~7003
skills 객체의 변수 영역
주소
7001
7002
7003
7004
데이터
이름: ‘HTML’ 값: @3008
이름: ‘CSS’ 값: @3005
이름: ‘JS’ 값: @3005
⇒ 이런 현상을 발생하지 않기위해 중첩된 객체에 대해서 불변 객체로 만들 필요가 있음

깊은 복사

내부의 모든 값들을 하나하나 찾아서 전부 복사하는 방법
중첩된 객체에 대한 깊은 복사
실행 결과
변수 영역
주소
1001
1002
1003
1004
데이터
이름: user 값: @3001
이름: user2 값: @3006
데이터 영역
주소
3001
3002
3003
3004
3005
3006
3007
3008
데이터
@5001~5002
‘lion’
‘lv 1’
'lv 2’
‘lv 3’
@5003~5004
‘jisu’
‘lv 5’
프로퍼티 변수 영역
주소
5001
5002
5003
5004
데이터
이름: name 값: @3002
이름: skills 값: @7001~7003
이름: name 값: @3007
이름: skills 값: @7004~7006
skills 객체의 변수 영역
주소
7001
7002
7003
7004
7005
7006
데이터
이름: ‘HTML’ 값: @3008
이름: ‘CSS’ 값: @3004
이름: ‘JS’ 값: @3005
이름: ‘HTML’ 값: @3003
이름: ‘CSS’ 값: @3005
이름: ‘JS’ 값: @3005

결론

어떤 객체를 복사하는 경우, 객체 내부의 모든 값을 복사해서 완전히 새로운 데이터를 만들고자 할 때, 객체의 프로퍼티 중에서 그 값이 기본형 데이터일 경우에는 그대로 복사하면 되지만 참조형 데이터는 다시 그 내부의 프로퍼티들을 복사해야함. 이 과정을 참조형 데이터가 있을 때마다 재귀적으로 수행해야만 비로소 깊은 복사가 됨
깊은 복사를 수행하는 함수
콘솔창에서 실행한 결과

깊은 복사를 처리할 수 있는 다른 방법들

hasOwnProperty 메서드를 활용해 프로토타입 체이닝을 통해 상속된 프로퍼티를 복사하지 않게끔 할 수 있음
객체를 JSON문법으로 표현된 문자열로 전환했다가 다시 JSON 객체로 바꾸는 것
→ 단순함에도 잘 동작함
→ But, 메서드(함수)나 숨겨진 프로퍼티인 __proto__나 getter/setter 등과 같이 JSON으로 변경할 수 없는 프로퍼티들은 모두 무시. (undefined, Infinity, -infinity, NaN 등은 JSON.stringify()를 통해 전달 불가능)
→객체 안에 순환 참조가 있을 경우 무한 루프에 빠지므로 주의가 필요(깊은 복사 수행 시 상황에 맞는 방법을 선택해야함)
→ httpRequest로 받은 데이터를 저장한 객체를 복사할 때 등 순수한 정보만 다룰 때 활용하기 좋음
JSON을 활용한 간단한 깊은 복사

정리하기

얕은 복사는 참조형 타입의 값의 바로 아래 단계의 값만 복사하는 방법
얕은 복사를 사용하여 객체를 복사하면 객체의 속성은 새로운 객체로 복사되지만, 속성값이 객체인 경우 참조 값이 복사되기 때문에 객체 내부의 값을 변경하면 원본 객체와 복사본 모두 변경
깊은 복사는 기존 값의 모든 참조가 끊어지는 것. 특히 복사할 때, 참조형 타입 값(객체)에서 내부에 있는 모든 값이 새로운 값이 되는 것
이 경우, 속성값이 객체인 경우에도 객체 내부의 값이 변경되어도 원본 객체에 영향을 미치지 않음