쫌만알자! (16) - 얕은 복사와 깊은 복사

Yeony (Nayeon Kim) · 2022-11-09

우리는 자바스크립트에서 값을 할당할 수 있다는 것을 압니다. 이미 할당된 변수에 다른 변수의 값을 재할당할 수 있다는 것도 알죠.

하지만 객체의 경우 메모리 상에서 참조(Reference) 된다는 것도 알았습니다.

let o1 = { name: 'yeony' } let o2 = o1 console.log(o1 === o2) // true

언뜻 보면 객체를 복사한 것처럼 보이지만 둘은 서로 같은 객체죠. 바로 같은 메모리 주소를 참조하고 있기 때문입니다.

이번 글에서는 참조되지 않는 복사를 한 번 알아봅시다.


얕은 복사 vs 깊은 복사 (Shallow Copy vs Deep Copy)

자바스크립트에서 할 수 있는 복사는 크게 두 종류로 나뉩니다. 얕은 복사깊은 복사입니다.

얕은 복사는 중접된 객체의 한 단계까지 복사하고, 깊은 복사는 중접된 객체까지 모두 복사합니다.

얕은 복사

얕은 복사는 참조된 값(객체의 메모리 주소)를 복사해 같은 객체를 참조하게 됩니다.

얕은 복사는 3가지 방법이 있습니다.

const obj = { objValue: 'objValue', nested: { key: 'value' } }

위 객체를 기준으로 살펴봅시다.

스프레드(Spread) 연산자

spread라는 단어의 의미는 펼치다, 퍼트리다입니다. 즉 전개하는 연산자입니다.

... 이렇게 점 3개를 연달아 작성하는 연산자인데요. ES6에 도입된 문법입니다. 이 연산자를 사용하면 객체의 프로퍼티를 펼쳐서 복사하는 것입니다. 객체와 배열에서 사용할 수 있습니다.

const obj = { objValue: 'objValue', nested: { key: 'value' } } const spread = { ...obj } // 서로 다른 객체 console.log(obj === spread) // false // obj의 중접 객체의 프로퍼티 값 변경 obj.nested.key = 'valueChanged' // spread의 중첩 객체 프로퍼티 값이 같이 변함 console.log(obj.nested.key === spread.nested.key) // true

복사된 객체와 원본 객체는 서로 다른 객체입니다. = 연산자로 할당했을 때 객체가 서로 같았던 것과는 확실히 다릅니다.

그럼에도 중첩된 객체의 값은 여전히 서로 참조되어 같이 움직입니다.

그렇다면 spread 객체에 새로운 프로퍼티를 추가해보면 어떻게 동작할까요?

spread.spreadOnly = 'onlySpreadValue' console.log(spread) // objValue: 'objValue', nested: {key: 'valueChanged', key2: 'value2'}, spreadOnly: 'onlySpreadValue!' console.log(obj) // objValue: 'objValue', nested: {key: 'valueChanged', key2: 'value2'}

spread 객체에만 값이 들어갔습니다! 앞서 말했듯 두 객체는 서로 다른 객체이지만, 중첩된 객체에 같은 참조를 가지고 있기 때문에 이러한 결과가 나온 것입니다.

레스트(rest)

나머지를 가져오는 방식입니다. 스프레드 연산자와 유사하게 생겼지만 엄연히 다릅니다.

rest는 객체, 배열, 함수의 파라미터에서 사용할 수 있습니다.

const obj = { objValue: 'objValue', nested: { key: 'value' } } const { objValue, ...rest } = obj console.log(rest) // nested: {key: 'value'} // obj의 중접 객체의 프로퍼티 값 변경 obj.nested.key = 'valueChanged' console.log(rest.nested.value) // 'valueChanged'

objValue를 제외한 나머지 프로퍼티들이 복사되었고, 중첩된 객체의 값은 여전히 참조되어 있는 것을 확인할 수 있습니다.

Object.assign()

Object.assign 은 자바스크립트의 내장 메소드입니다.

Object.assign(타깃, 원본 객체)로 작성하면 얕은 복사된 객체를 돌려줍니다.

const obj = { objValue: 'objValue', nested: { key: 'value' } } const assigned = Object.assign({}, obj) console.log(obj === assigned) // false // obj의 중접 객체의 프로퍼티 값 변경 obj.nested.key = 'valueChanged' console.log(assigned.nested.value) // 'valueChanged'

역시나 중첩된 객체 값은 참조되어 있습니다.

깊은 복사

얕은 복사는 중첩된 객체의 경우 계속 참조되는 것을 보았습니다.

반면 깊은 복사는 얕은 복사와는 달리, 원본과 복사본 중 어떤 값을 바꾸어도 서로에 영향이 없다는 것을 보증합니다.

자바스크립트에서 깊은 복사를 생성하는 방법은 한 가지입니다.

아직은 낯선 개념이겠지만 언급하고 넘어갑니다.

JSON.stringify()와 JSON.parse() 사용

JSON은 JavaScript Object Notation인데, 이후에 자세히 알아보도록 하겠습니다.

JSON.stringify()는 이름에서 유추할 수 있듯이 자바스크립트 값 혹은 객체를 JSON 문자열로 변환합니다.

JSON.parse()는 JSON 문자열의 구문을 **분석(parse)**하고 그 결과를 바탕으로 자바스크립트 값이나 객체를 생성합니다.

const obj = { objValue: 'objValue', nested: { key: 'value' } } JSON.stringify(obj) // '{"objValue":"objValue","nested":{"key":"value"}}' const deepCopy = JSON.parse(JSON.stringify(obj)) // obj의 중접 객체의 프로퍼티 값 변경 obj.nested.key = 'valueChanged' // 변하지 않은 deepCopy의 중첩 객체 값 console.log(deepCopy.nested.value) // 'value'

원본 객체를 수정하였지만 deepCopy 객체의 값은 아무것도 달라지지 않았습니다.

다음 글에서는 함수를 알아보겠습니다.

Reference

3 Ways to Shallow Clone Objects in JavaScript
Shallow Copy vs Deep Copy in JavaScript

자바스크립트의 메모리 👈 이전 글 보기
함수 👈 다음 글 보기


쫌만알자
Loading script...
© 2022 Nayeon Yeony Kim