Nested Object(중첩된 객체)의 deep copy 방법

빠샤빠샤·2023년 7월 4일

JavaScript

목록 보기
2/2
post-thumbnail

Nested Object(중첩된 객체)의 경우 단순 spread operator(...)만으로는 온전한 deep copy가 불가능하다. 왜냐하면 내부 객체도 하나하나 다 spread operator를 사용해 deep copy를 해주지 않으면 결국 내부 객체들은 shallow copy밖에 되지 않는다.

예를들어 아래와 같은 Nested Object가 있다고 해보자.

const animal = {
	person: 'diverse',
	dog: {
		personality: 'friendly',
		age: { max: 20, min: 0 },
		species: ['phome', 'retreive', 'martiz'],
	},
	cat: {
		personality: 'attractive',
		age: { max: 20, min: 0 },
		species: ['spinks', 'gilnyang', 'gaenyang'],
	},
};

만일 상수 animal을 다른 상수에 단순 spread operator로만 deep copy를 한다고 했을 때

const newAnimal = {...animal}

새로운 상수 newAnimal은 상수 animal의 deep copy를 제대로 한 것이 아니다.

animal.person = 'amazing creation'
animal.dog.personality = 'too friendly sometimes'

상수 animal의 가장 상위 객체인 person 값을 변경시키는 경우에는 상수 newAnimal에는 적용이 되지 않지만, 하위 객체인 dog.personality의 값을 변경시키는 경우에는 상수 newAnimal에도 변경된 값이 적용이 된다. (하위 객체는 deep copy가 아닌 shallow copy가 되어 상수 animal의 주소값을 보고있기 때문)


중첩된 객체를 모두 deep copy를 하기 위해서는 JSON stringify, JSON parse를 사용할 수도 있고

const newAnimal = JSON.parse(JSON.stringify(animal))

아래와 같이 중첩된 객체를 deep copy하는 함수를 직접 만들어 놓고 사용해도 된다. (chatgpt 활용)

export function deepCopy(source: any): any {
    if (Array.isArray(source)) {
        // Deep copy an array
        return source.map((item: any) => deepCopy(item));
        
    } else if (typeof source === 'object' && source !== null) {
        // Deep copy an object
        const copiedObject: any = {};
        for (const key in source) {
            if (Object.prototype.hasOwnProperty.call(source, key)) {
                copiedObject[key] = deepCopy(source[key]);
            }
        }
        return copiedObject;
    }
    
    // Return primitive values directly
    return source;
}

이 함수는 매개변수로 주어진 "source"를 받아온다.

만약 "source"가 배열인 경우, 배열의 각 항목에 대해 "deepCopy" 함수를 재귀적으로 호출하여 깊은 복사를 수행한 후, 새로운 배열을 반환한다.

그렇지 않고 "source"가 객체이면서 null이 아닌 경우, "copiedObject"라는 새로운 객체를 생성한다. 그리고 "source"의 각 속성을 순회하면서 해당 속성의 값을 "deepCopy" 함수를 재귀적으로 호출하여 깊은 복사한 후, "copiedObject"에 대입한다. 마지막으로 "copiedObject"를 반환한다.

만약 "source"가 배열이나 객체가 아닌 경우, 즉 원시값인 경우에는 그대로 반환한다.

profile
UI/UX Designer & Frontend Developer

0개의 댓글