[자바스크립트] 얕은 복사, 깊은 복사 간단히 살펴보기(Learn Deep & Shallow Copy with Tricky JavaScript Questions)
하수도키
·2021. 3. 3. 13:32
이 포스팅은 아래 포스팅을 보고 의역한 겁니다. 자세한 내용은 링크를 참고해주세요.
javascript.plainenglish.io/learn-deep-shallow-copy-with-tricky-javascript-questions-c563cdb5a4dd
얕은 복사(Shallow Copy), 깊은 복사(Deep Copy)는 자바스크립트 책을 볼 때, 간간이 들려오는 질문들에 항상 포함되어 있다. 그때마다 구글링을 하거나 대충 기억을 더듬더듬 거리면서 이해하는 걸로 넘어갔다.
운 좋게 간단하고 이해하기 쉬운 포스팅이 있어 정리할 겸 포스팅을 작성해보겠다.
우선 아래 코드들을 보고 차이점을 살펴보자.
const flower = [{ name: 'Lily', color: 'white' }]
const clone = [...flower]
clone[0].name = 'Camellia'
console.log(flower)
// [{name: "Camellia", color: "white"}]
console.log(clone)
// [{name: "Camellia", color: "white"}]
위 코드를 살펴보면, 복사된 clone 에서 name을 "Camellia"로 변경했더니, 원본인 flower의 name도 "Camellia"로 변경되었다.
const flower = { name: 'Lily', color: 'white' }
const clone = { ...flower }
clone.name = 'Camellia'
console.log(flower)
// {name: "Lily", color: "white"}
console.log(clone)
// {name: "Camellia", color: "white"}
위 코드는 복사 된 clone에서 name을 "Camellia"로 변경했더니, 원본인 flower에서는 변하지 않고 "Lily" 그대로인걸 알 수 있다.
코드 차이점을 눈치챘습니까?
첫 번째는 flower는 배열안에 중첩된 객체를 가지고 있다. 반면에, 두번째 flower는 배열이 없고 바로 객체를 가지고 있다.
첫번째는 바로 얕은 복사(shallow copy)가 되었다.
얕은 복사는 중첩되어 있는 객체들은 복사하지 않고 가장 상위에 있는 것들만 복사한다. 따라서 중첩된 객체들은 원본 객체들과 같은 메모리 포인트를 참조한다.
그 결과, 복사된 clone에서 name을 변경하니 원본인 flower도 변경된다.
두 번째는 바로 깊은 복사(Deep copy)가 실행되었다.
이건 가장 상위에 있는 것만 복사하는 게 아니고 중첩된 객체들까지 모두 복사한다.
그래서 새로운 메로리 포인트를 생성되므로 기존 원본하고 충돌이 나지 않는다.
이제 이미지로 얕은 복사, 깊은 복사를 더 다뤄보자.
얕은 복사 이미지로 살펴보기(Shallow Copy Analogy)
아래 이미지를 통해 얕은 복사를 잘 살펴보자.
원본이 obj1이고 obj1를 복사한 게 clone이다. (속성으로는 잎의 색상을 나타나는 { color: "green" } 있다고 생각하자.)
복사되면서 화분이 1개 더 생기는 게 아니고 같은 화분 속에서 clone이 복사되었다.
이제 복사된 clone에 잎의 색상을 "pink"로 변경하고 싶어서 clone객체를 { color: "pink" }로 수정하면 obj1의 잎의 색상도 아래 이미지처럼 같이 변경된다. 그 이유는 같은 화분 속에 있기 때문에, 즉 같은 obj 메모리를 참조하고 있다는 뜻이다.
이제 깊은 복사를 이미지를 통해 살펴보자.
깊은 복사 이미지로 살펴보기(Deep Copy Analogy)
위에서 이미지 얕은 복사로 설명했으니 깊은 복사는 이미지를 통해 좀 더 간단히 설명하겠다.
얕은 복사에서는 같은 화분에 꽃이 심어져 있었다.
아래 사진은 깊은 복사 이미지이며 각각 꽃에 화분이 따로 있다.
하나의 화분이 아닌 2개의 화분이다. 얕은 복사처럼 같은 화분에 복사된 꽃만 심어져 있는 게 아니고 화분, 꽃 둘 다 각각 복사되어 새로운 화분에 꽃이 심어져 있다.
clone에 잎의 색상을 역시 변경하기 위해 {color: "pink"}로 변경하면 obj1의 잎의 색상을 변경되지 않고 clone의 잎의 색상만 변경된다.
깊은 복사가 이루어지면 새로운 화분이 만들어지면서 메모리 참조가 달라졌기 때문이다.
이제 복사하는 방법을 알아보자.
복사하는 방법(How about other cloning mehods?)
위에서는 ES6 전개 연산자로 복사했다.
복사하는 다른 방법인 Object.assign()과 JSON.parse(JSON.stringify(object)), Lodash cloneDeep를 이용하는 방법을 살펴보자.
Object.assign()
전개 연산자와 비슷하다.
데이터가 중첩되어 있으면 얕은 복사, 중첩되어 있지 않으면 깊은 복사가 이루어진다.
얕은 복사
// shallow copy because the object is nested
const flower2 = [{name:'Lily', color:'white'}]
const clone2 = Object.assign({}, flower2)
console.log(flower2[0] == clone2[0]) // true
clone2[0].name = "Tulip"
console.log(flower2[0].name) // "Tulip"
console.log(clone2[0].name) // "Tulip"
깊은 복사
// deep copy because the object is nested
const flower = {name:'Lily', color:'white'}
const clone = Object.assign({}, flower)
console.log(flower == clone) // true
clone.name = "Camellia"
console.log(flower.name) // "Lily"
console.log(clone.name) // "Camellia"
JSON.parse(JSON.stringify(object))
이 메서드는 중첩된 데이터가 있던 말던 무조건 깊은 복사를 한다.
JSON.parse(JSON.stringify(object)) 중첩된 데이터
// object is nested
const flower2 = [{name:'Lily', color:'white'}]
const clone2 = JSON.parse(JSON.stringify(flower2))
console.log(flower2[0] == clone2[0]) // true
clone2[0].name = "Tulip"
console.log(flower2[0].name) // "Tulip"
console.log(clone2[0].name) // "Tulip"
JSON.parse(JSON.stringify(object)) 중첩되지 않은 데이터
// object is NOT nested
const flower = {name:'Lily', color:'white'}
const clone = JSON.parse(JSON.stringify(flower))
console.log(flower == clone) // true
clone.name = "Camellia"
console.log(flower.name) // "Lily"
console.log(clone.name) // "Camellia"
Lodash cloneDeep
만약 위와 같이 하는 게 너무 귀찮고 복잡하다고 생각되면 Lodash를 사용하면 무척 편해진다.
이건 아래 링크를 참고하면 된다.
lodash.com/docs/4.17.15#cloneDeep
결론
얕은 복사와 깊은 복사에 대해 진짜 간편하고 직관적인 포스팅이라서 정리하면서 이해가 더욱 잘 된 것 같다.
더 깊게 들어가고 싶으면 구글링 검색하면서 더 많은 아티클과 자료들이 있으니 참고해보자.
'개발일기 > Javascript' 카테고리의 다른 글
JavaScript - DOM이란? (0) | 2021.11.03 |
---|---|
Javascript timezone, language 구하기(타임존, 언어, 국가) (0) | 2021.09.11 |
[Javascript] URL페이지를 Location, Redirect, Reload 통해 살펴보자. (0) | 2021.06.15 |
[Javascript] replace를 이용해 문자열을 치환하는 방법(How to replace a string in javascript) (0) | 2021.01.20 |
디버깅에 유용한 콘솔(Console) 사용법 알아보기 (2) | 2020.11.05 |