Learn Shallow&Deep Copy

Junghan Lee·2023년 3월 31일
0

Learnd in Camp

목록 보기
20/48

index

객체&배열 복사(객체 복사, 스프레드 연산자, 깊은 복사, lodash, 배열 복사)

객체, 배열 복사

복사? 얕은 복사(Shallow Copy) & 깊은 복사(Deep Copy)

복사의 기본 개념

let aaa = "철수"
let bbb = aaa

console.log(aaa) // 철수
console.log(bbb) // 철수

복사본의 값 변경, 재할당

bbb = "영희"

console.log(aaa) // 철수
console.log(bbb) // 영희

원본aaa 변하지 않고 복사본인 bbb의 값만 변함

객체의 복사

let child1 = {
	name: "철수",
	age: 8,
	school: "다람쥐초등학교"
}
let child2 = child1

child2 // {name: '철수', age: 8, school: '다람쥐초등학교'}

복사본인 child2 객체의 name 변경시

child2.name = "영희"

child1 // {name: '영희', age: 8, school: '다람쥐초등학교'}
child2 // {name: '영희', age: 8, school: '다람쥐초등학교'}

child2의 값만 변경했는데 child1의 값은 왜 변경되었을까? 데이터 타입의 특징 때문


String, Number, Boolean 타입 데이터 : 변수에 값을 할당하면 값 자체가 저장되는 반면
객체와 배열의 경우 값 자체가 아닌 '주소'가 저장된다.

객체를 복사한 뒤 값을 변경할 때, 복사는 문제없이 잘 이루어진 것이었다. child1에 할당된 객체의 주소 값이 child2에도 동일하게 들어가게 된 것

그러나 두 객체가 같은 주소를 가지고 있어서 child2의 값을 변경하면 child1의 값도 같이 변경되었던 것

위와 같은 문제 없이 객체를 복사하기 위해?
사실 객체 복사라는 것은 존재하지 않는다.
원본 객체와 같은 값을 가진 객체를 새로 만드는 개념

// child3에 child2 복사
let child3 = {
    name: child2.name,
    age: child2.age,
    school: child2.school
}

child3 // {name: '영희', age: 8, school: '다람쥐초등학교'}
// child3의 name 변경
child3.name = "훈이"

child3 // {name: '훈이', age: 8, school: '다람쥐초등학교'}
child2 // {name: '영희', age: 8, school: '다람쥐초등학교'}

위처럼 child2의 객체의 각 값을 꺼내 child3의 각 key에 할당해주면 child2를 child3에 복사한 것과 같은 모습으로 엄밀히 말해 복사는 아니지만 이것이 객체 복사

객체 복사 수행 뒤, 복사본인 child3의 값을 변경해도 child2의 name값은 변경되지 않고 유지된다.

객체를 복사할 때마다 원본 객체의 모든 값을 따로따로 가져오기 번거롭기 때문에...

스프레드 연산자

마침표 세개를 연속해 찍어주면, 해당 객체 내의 모든 값을 개별 요소로 분리할 수 있다.

스프레드 연산자를 이용해 객체를 복사하게 되면
복사본의 값을 변경해도 원본 값이 변경되지 않는다.

let child4 = {
	...child2
}

child4 // {name: '영희', age: 8, school: '다람쥐초등학교'}

스프레드 연산자를 통한 중첩 객체 복사
그러나 스프레드 연산자를 통한 복사도 만능은 아니다.
객체 안에 객체가 값으로 들어가 있는 경우 제대로 복사되지 않는다.

 let profile1 = {
    name: "철수",
    age: 8,
    school: "공룡초등학교",
    hobby: {
        first: "수영",
        second: "프로그래밍"
    }
}

let profile2 = {
    ...profile1
}
profile1.name = "영희"

profile1 // {name: '영희', age: 8, school: '공룡초등학교', hobby: {…}}
profile2 // {name: '철수', age: 8, school: '공룡초등학교', hobby: {…}}

스프레드 연산자를 이용한 객체 복사 뒤에 profile1의 name값을 변경해도 profile2의 name값이 변하지 않는다.

그러나 hobby를 변경했을 때는

복사가 제대로 되지 않았다는 것을 알 수 있다.

hobby라는 key에 대한 값도 주소값으로 들어가있기 때문에 이런 문제가 발생한다. 이처럼 스프레드 연산자를 이용한 복사를 얕은 복사라고 한다.

얕은 복사는 depth1의 깊이를 가진 데이터까지는 복사할 수 있으나 그 이상의 깊이를 가진 데이터를 복사하지 못한다.

depth2 이상의 데이터를 복사하기 위해서는

깊은 복사

를 해야 한다.

객체를 문자열의 형태로 바꾸고 그 문자열을 다시 객체로 바꾸어 새로운 변수에 담는 것이다. JSON.stringify, JSON.parse 메소드를 이용하면 객체&배열을 문자열로, 문자열을 객체&배열로 바꿀 수 있다.

여기서 JSON은 JavaScript Object Notation의 약자로 Javascript 객체 문법으로 구조화된 데이터를 표현하기 위한 문자 기반의 표준 포맷이다.

// JSON.stringify
JSON.stringify(profile1)
// '{"name":"철수","age":8,"school":"공룡초등학교","hobby":{"first":"수영","second":"프로그래밍"}}'
// JSON.parse
JSON.parse(JSON.stringify(profile1))
// {name: '철수', age: 8, school: '공룡초등학교', hobby: {first: '수영', second: '프로그래밍'}}

lodash

객체를 복사할 때마다 깊은 복사를 따로 해주는 것은 번고롭고 느릴 수 있다. 그래서 관련 작업을 도와주는 라이브러리를 이용하는 방법이 있다.

그중 다수가 사용하는 lodash가 있다.

https://lodash.com/docs/4.17.15
lodash에서 제공하는 _.cloneDeep(value)를 사용하면 깊은 복사를 할 수 있다.
참고로 lodash는 이외에도 다양한 기능을 제공한다.

배열 복사

그렇다면 객체가 아닌 배열은 어떻게 복사해야할까?
객체와 같은 방식으로 복사가 가능하다.

const aaa = ["철수", "영희", "훈이"]
const bbb= [...aaa]
profile
Strive for greatness

0개의 댓글