정리용/js

[js 자주 사용되는 ES6+문법] 얕은 복사, 깊은 복사

hee-ya07 2025. 1. 10. 18:00

1. 얕은 복사 (Shallow Copy)

: 객체나 배열의 첫번째 수준 복사

: 객체의 최상위 값만 복사, 내부 참조된 객체나 배열은 복사X → 원본과 동일한 참조 

 

1.1 얕은 복사 예시

1) 배열의 얕은 복사

const arr = [1, 2, 3];
const shallowCopy = arr.slice(); // 또는 [...arr]

shallowCopy[0] = 100;
console.log(arr); // [1, 2, 3] - 원본 배열은 변경되지 않음
console.log(shallowCopy); // [100, 2, 3] - 복사본은 변경됨

arr[0] = 90;
console.log(arr); // [90, 2, 3] 
console.log(shallowCopy); // [100, 2, 3]

 

2) 객체의 얕은 복사

const obj = { a: 1, b: 2 };
const shallowCopy = { ...obj };

shallowCopy.a = 100;
console.log(obj); // { a: 1, b: 2 } - 원본 객체는 변경되지 않음
console.log(shallowCopy); // { a: 100, b: 2 } - 복사본은 변경됨

obj.a = 90;
console.log(obj); // { a: 90, b: 2 }
nsole.log(shallowCopy); // { a: 100, b: 2 }

 

1.3 얕은 복사의 단점

: 앝은 복사의 경우 1차원적인 것은 각각 독립적으로 바뀜

: But, 2차원으로 들어갈 경우(중첩된 객체 또는 배열) 그 내부 값 변경 시, 서로 영향

 

1) 복사본의 값을 변경할 경우

const obj = { a: 1, b: 2, c: { d: 4 } };
const shallowCopy = { ...obj };

shallowCopy.c.d = 100;
console.log(obj); // { a: 1, b: 2, c: { d: 100 } } - 원본도 변경
console.log(shallowCopy); // { a: 1, b: 2, c: { d: 100 } }

 

2) 원본 값을 변경할 경우

const obj = { a: 1, b: 2, c: { d: 4 } };
const shallowCopy = { ...obj };

obj.c.d = 100;
console.log(obj); // { a: 1, b: 2, c: { d: 100 } } 
console.log(shallowCopy); // { a: 1, b: 2, c: { d: 100 } } - 복사본도 변경

2. Object.assign() VS Spread Syntax (배열 및 객체 펼치기)

2.1 Object.assign()

: 과거에 사용된 앝은 복사 방법

: 1번째 인수는 대상 객체, 두번째 이후는 복사하려는 객체

const object = Object.assign({}, { name: 'Aaron', age: 10 })
const refers = object
const copied = Object.assign({}, object)

console.log(object)
console.log(refers)
console.log(object === refers) //true

console.log(object)
console.log(copied)
console.log(object === copied) // false

더 간단한 예시는

const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const mergedObj = Object.assign({}, obj1, obj2);

console.log(mergedObj);  // { a: 1, b: 2, c: 3, d: 4 }

2.2 Spread Syntax

2-1) 배열 펼치기: 배열 복사 + 배열 연결 + 배열 요소 추가

const array = [1, 2, 3]
//    array = [1, 2, 3]
// ...array =  1, 2, 3  => 대괄호 [] 제거
const added = [...array, 4, 5]
console.log(added)
/* [ 1, 2, 3, 4, 5 ] */

 

2-2) 객체 펼치기: 객체 복사 + 객체 연결 + 객체 프로퍼티(Property) 추가

const object = { name: 'Aaron', age: 10 }
//    object = { name: 'Aaron', age: 10 }
// ...object =   name: 'Aaron', age: 10   => 중괄호 {} 제거
const modified = {...object, name: 'Baron'}
console.log(modified)
/* { name: 'Baron', age: 10 } */

2.3 비교

특성 Spread Syntax Object.assign()
주요 용도 배열 및 객체 복사, 병합 객체 복사 및 병합
앝은 복사 O O
배열 사용 여부 O O
객체 병합 O O
참조 복사 여부 중첩된 객체 or 배열에 대한 참조 복사 중첩된 객체 or 배열에 대한 참조 복사
반환값 새로운 배열 or 객체 새로운 객체(=새 병합 객체)
문법 간결, 직관 길고, 명시적

3. 깊은 복사

: 객체나 배열 내의 모든 값을 재귀적으로 복사하여 새로운 객체를 생성

: 원본 객체와 복사본은 완전히 독립적인 객체

더보기

- 깊은 복사 → 직렬화 || 역직렬화

 

: 객체 or 배열에서 중첩된 배열이 있을 경우

: JSON.parse() 와 JSON.stringify()를 활용하여 깊은 복사 가능


3.1 깊은 복사 예시

1. 배열의 깊은 복사

const arr = [1, 2, [3, 4]];
const deepCopy = JSON.parse(JSON.stringify(arr));

deepCopy[2][0] = 100;
console.log(arr); // [1, 2, [3, 4]] - 원본 배열은 변경되지 않음
console.log(deepCopy); // [1, 2, [100, 4]] - 복사본은 변경됨

But, 함수나 undefind, 순환 참조를 포함한 객체에서는 동작하지 않을 수 있음.

 

2. 객체의 깊은 복사

const obj = { a: 1, b: { c: 2 } };
const deepCopy = JSON.parse(JSON.stringify(obj));

deepCopy.b.c = 100;
console.log(obj); // { a: 1, b: { c: 2 } } - 원본 객체는 변경되지 않음
console.log(deepCopy); // { a: 1, b: { c: 100 } } - 복사본은 변경됨

 

3. 수동으로 깊은 복사

⇒ 복잡한 객체 or 배열의 경우, 재귀를 사용하여 깊은 복사

function deepClone(obj) {
  if (typeof obj !== 'object' || obj === null) {
    return obj; // 객체가 아니면 그대로 반환
  }

  const clone = Array.isArray(obj) ? [] : {}; // 배열과 객체 구분
  for (const key in obj) {
    clone[key] = deepClone(obj[key]); // 재귀 호출로 깊은 복사
  }
  return clone;
}

const original = { a: 1, b: { c: 2 } };
const deepCopy = deepClone(original);

deepCopy.b.c = 100;
console.log(original); // { a: 1, b: { c: 2 } }
console.log(deepCopy); // { a: 1, b: { c: 100 } }

 

 


참조

ASAC 수업자료