오브젝트 복사와 참조 깊은복사 얕은복사 이해

오늘은 오브젝트 복사에 대해 알아봅니다.

원시값은 복사되고 오브젝트는 참조가 된다. 이말이 매우 중요함. 즉 숫자,문자,불린은 값자체가 복사되고 오브젝트,배열,함수는 단지 참조만 된다. 참조가 되면 참조한 값을 바꾸면 원본도 같이 바뀌게 된다. 이말이 이해가 안되어도 아래를 보면 이해 할 수 있다.

원시형 자료형인 숫자, 문자, 불린은 복사를 해도 연결되어 있지 않아서 우리가 보통 알고 있는 방식으로 값이 복사가 된다. 아래의 예를 보면 이해가 쉽다.

var aa = '우리집'; var bb = aa; //aa를 bb에 복사했음. console.log(bb) //결과: '우리집' bb = '당신집'; //bb에 다른 값을 넣었다. console.log(aa,bb); //결과: '우리집' '당신집'
Code language: JavaScript (javascript)

위의 그림과 같이 복사를 하고 값을 바꾸면 aa의 값은 그대로 있고 bb의 값만 바뀐다. 즉, aa와 bb의 값은 서로 연결되어 있지 않고 독립적으로 있다.

오브젝트에서의 복사

하지만 자료형이 Object라면 말이 달라진다. 오브젝트 자료형은 오브젝트, 배열, 함수가 있다. 이 세가지는 복사를 하면 원본의 값과 연결되어 있어서 복사한 변수의 값을 변경하면 원본도 같이 변경되어 버린다.

var obj = {a:1, b:2, c:3} var copyObj = obj console.log(obj,copyObj) //결과: {a: 1, b: 2, c: 3} {a: 1, b: 2, c: 3} copyObj.a = "학교"; console.log(obj,copyObj); //결과:{a: "학교", b: 2, c: 3} {a: "학교", b: 2, c: 3}
Code language: JavaScript (javascript)
javascript 복사

위와 같이 오브젝트는 복사한 곳에서 값을 변경하면 원본도 같이 변경되어 버리는 것을 알 수 있다.

오브젝트 복사 방법 1

그렇다면 원본 변경없이 복사를 하고 싶다면 어떻게 할까. forEach함수를 사용해서 각각의 값을 복사해서 가져오는 방법이 있다.

var obj = {a:'감', b:'배', c:'귤'}; var copyObj = {}; Object.keys(obj).forEach(function(key){ copyObj[key] = obj[key]; }); console.log(obj,copyObj); //결과: {a:'감', b:'배', c:'귤'}{a:'감', b:'배', c:'귤'} copyObj.a = '레몬'; console.log(obj,copyObj); //결과: {a:'감', b:'배', c:'귤'}{a:'레몬', b:'배', c:'귤'}
Code language: JavaScript (javascript)

하지만 위의 방법도 오브젝트 내부에 오브젝트가 있는 형태라면 제대로 작동을 하지 않는다.

예를들면 {a:1, b:{c:1}} 이런식으로 내부에 오브젝트를 가지고 있는 경우이다. 이 경우 forEach가 반복을 하면서 a:1은 정상적으로 참조가 끊어지고 값이 복사된다. 하지만 b:{c:1}에 가서는 오브젝트 형태이기 때문에 원본을 참조만 하고 끝난다.

위의 객체안에 있는 객체를 forEach반복문을 사용했을때 실행과정을 일반문으로 풀어쓰면 아래와 같이 된다.

var obj = {a:1, b:{c:1}}; var copyObj = {}; copyObj.a = obj.a; //첫번째 반복 원시형 copyObj.b = obj.b; //두번째 반복 object형
Code language: JavaScript (javascript)

위와 같이 내부적으로 반복문을 돌기 때문에 처음에 말했듯이 원시형은 참조가 끊어지지만 두번째 오브젝트형에서는 다시 참조를 해버린다.

var obj = {a:'감', b:'배', c:'귤'}; var copyObj = {}; Object.assign(copyObj, obj) //이렇게도 된다. console.log(copyObj,obj);
Code language: JavaScript (javascript)

참고로 위와 같이 Object.assign()을 사용하면 한줄로도 처리할 수 있다.

얕은복사 –> 참조, 깊은복사 –> 복사 라고 볼 수 있다.

오브젝트 복사 방법 2

그래서 다른 방법으로 JSON.parse(JSON.stringify())를 이용해 볼 수 있다. 그나마 가장 간단한 방법이다.

var obj = {a:1, b:{c:1}}; var copyObj = JSON.parse(JSON.stringify(obj)); copyObj.b.c = 9; console.log(obj.b.c, copyObj.b.c); //결과: 1, 9
Code language: JavaScript (javascript)

위와 같이 참조 관계가 끊긴 것을 볼 수 있다.

배열같은 경우에는 좀더 쉬운 방법으로 slice()함수를 사용하는 방법이 있다. 하지만 이 방법은 배열내부에 오브젝트가 있으면 forEach와 마찬가지로 1단계복사뿐이 안되어서 완전한 복사가 이루지지는 않는다.

var arr = [1,3,4]; var copyArr = arr.slice(); copyArr[0] = 8; console.log(arr, copyArr); //결과: [1,3,4] [8,3,4]
Code language: JavaScript (javascript)
var arr = [1,3,4,{a:{b:{c:5}}}]; var copyArr = JSON.parse(JSON.stringify(arr)); copyArr[3].a.b.c = 9; console.log(arr[3].a.b.c) // 5 console.log(copyArr[3].a.b.c) //9
Code language: JavaScript (javascript)

JSON 함수를 사용하면 깊은 복사를 할 수 있다. 내부에 객체가 있더라도 참조가 아닌 복사가 이루어진다. 하지만 이 함수를 사용하게되면 성능이 안좋아진다.

너무 길어져서 오늘은 여기까지만 하겠습니다.

답글 남기기

5 + 15 =