[JavaScript] 얕은 복사와 깊은 복사

진세오·2025년 7월 5일

복사

    얕은복사와 깊은복사는 객체의 복사 방법 입니다. 얕은 복사(shallow copy)는 바로 아래 단계의 값만 복사하고, 깊은 복사(deep copy)는 내부의 모든 값들을 하나씩 전부 복사합니다. 기본적으로 복사는 할당(=)을 통해 할 수 있는데, 기본형과 객체(참조형)는 복사를 할때 다르게 동작을 하게 됩니다. 예를 들어보겠습니다.

//기본형 복사
let a = 10;
let b = 0;
b = a;
b = 14;

console.log(a); // 10
console.log(b); // 14


// 객체 복사
let a = {
  name : 'sally',
  age : 25
}
let b = a;
b.age : 30;

console.log(a); // {name: 'sally', age: 30}
console.log(b); // {name: 'sally', age: 30}

    기본형의 경우 복사 이후 변경되는 내용이 원본에 영향을 주지 않지만, 객체복사의 경우 b의 값을 변경했는데 a까지 변경되는 것을 알 수 있습니다. 참조형 타입인 객체는 기본형과 달리 위의 코드처럼 복사하게 된다면 얕은 복사가 진행이 되어 객체 a와 b는 같은 데이터를 바라보고 있게 됩니다.

얕은 복사

    객체의 얕은 복사는 복사본의 속성이 원본 객체와 같은 참조를 공유하는 복사입니다. 따라서 원본이나 복사본을 변경하면, 참조되어있는 값이 바뀌기 때문에 해당 값을 참조하는 모든 객체가 변경됩니다.

위에서 간단하게 예시를 들었던 복사코드를 아래와 같이 변경하면 어떻게 될까요?

// 할당연산자 사용
let a = {
  name : 'sally',
  age : 25
}
let b = a;
b.age : 30;

console.log(a.age === b.age) // true
console.log(a); // {name: 'sally', age: 30}
console.log(b); // {name: 'sally', age: 30}

// 객체 메소드 사용
let a = {
  name : 'sally',
  age : 25
}
let b = Object.assign({}, a);
b.age : 30;

console.log(a.age === b.age) // false
console.log(a); // {name: 'sally', age: 25}
console.log(b); // {name: 'sally', age: 30}

    a와 b의 age값이 바뀐것을 볼 수 있습니다. 객체의 메소드를 사용하니 원본과 복사본이 다른 객체로 생성되었다는 것을 볼 수 있습니다. 그렇다면 중첩 객체의 경우엔 어떻게 진행이 되는지 보겠습니다.

// 할당연산자 사용
let a = {
  name : 'sally',
  age : 25,
  sns : {
    instagram : '@a_insta',
    blog : 'velog.io/@a_dev/posts'
  }
}

let b = Object.assign({}, a);
b.sns.blog : 'blog.naver.com/b_dev';

console.log(a.sns.blog === b.sns.blog) // true

    a객체 안에 있는 sns객체는 또 다시 얕은 복사가 수행되어 원본과 복사본의 일치가 발생하게 됩니다. Object.assign() 메소드는 속성의 값을 복사하기 때문에, 얕은 복사가 이루어진 것입니다. 얕은 복사가 발생되는 메소드들은 다음과 같이 있습니다.

  1. Object.assign()
  2. Object.create()
  3. Spread 연산자(전개 연산자)
  4. Array.prototype.slice()
  5. Array.prototype.concat()
  6. Array.from()



깊은 복사

    객체의 깊은 복사는 복사본의 속성의 원본 객체와 다른 참조를 바라보는 복사입니다. 따라서 복사된 객체와 원본 객체는 완전히 다른 객체라고 얘기할 수 있습니다. 얕은 복사는 중첩된 객체를 제대로 복사하지 못하는 특징이 있습니다. 그렇기 때문에 중첩된 객체를 복사하기 위해서 깊은 복사를 어떻게 하는지 알아야 합니다.

  1. JSON.parse && JSON.stringify
      JSON.stringify()는 객체를 json 문자열로 변환하는데 이 과정에서 원본 객체와의 참조가 모두 끊어집니다. 객체를 json 문자열로 변환 후, JSON.parse()를 이용해 다시 원래 객체(자바스크립트 객체)로 만들어줍니다. 이 방법이 가장 간단하고 쉽지만 다른 방법에 비해 느리다는 것과 객체가 function일 경우, undefined로 처리한다는 것이 단점입니다.

  2. 재귀함수
let copyObjectDeep = function(target) {
  let result = {};
  if( typeof target === 'object' && target !== null){
    for (let prop in target){
      result[prop] = copyObjectDeep(target[prop]);
    }
  } else {
    result = target;
  }
  return result;
}

    재귀 함수를 통해 원본 객체의 프로퍼티 중 객체가 있다면 함수를 호출하고, 객체가 아니라면 프로퍼티를 그대로 담아서 해당 result 객체를 반환하는 함수를 만들 수 있습니다. 재귀함수를 만드는 것이 복잡하다는 단점이 있습니다.



참고자료

0개의 댓글