[JavaScript] 객체 복사하기

원시형과 객체의 복사 차이

원시형

문자열, 숫자, 불린 등은 변수에 값 자체가 저장됩니다.

let message1 = "Hello";
let message2 = message1
// meesage1과 message2는 각각 별도의 메모리 공간에 Hello라는 값을 담고 잇습니다.

객체형

객체는 변수에 실제 데이터가 아닌, 메모리 상의 주소(참조값)가 저장됩니다. 따라서 같은 객체를 가리키는 여러 변수가 있을 수 있으며, 한 변수에서 객체를 변경하면 다른 변수에서도 그 변경이 반영됩니다.

let car = { brand: "Toyota" };
let vehicle = car; // car의 참조값이 vehicle에 복사됨

vehicle.brand = "Honda"; // vehicle을 통해 객체의 brand 프로퍼티를 수정
console.log(car.brand); // "Honda" – car와 vehicle은 동일 객체를 참조함

객체 비교

동일한 객체 참조

같은 객체를 참조하는 두 변수는 비교 연산자(==, ===)로 비교 시 true를 반환합니다.

let a = {};
let b = a;  // 동일한 객체 참조
console.log(a === b);  // true

서로 다른 객체 참조

내용이 같더라도 서로 독립적으로 생성된 객체는 비교 시 false를 반환합니다.

let a = {};
let b = {};
console.log(a === b);  // false, 내용은 같지만 별개의 객체

객체 복사

얕은 복사(Shallow Copy)

변수에 객체를 단순 대입하면 참조값만 복사되므로, 독립된 객체를 얻으려면 프로퍼티 단위로 복사해야 합니다.

for ...in 반복문을 이용한 복사

let book = {
  title: "1984",
  author: "George Orwell"
};

let copy = {}; // 새로운 빈 객체

for (let key in book) {
  copy[key] = book[key]; // 각 프로퍼티를 복사
}

copy.title = "Animal Farm";
console.log(book.title); // "1984" – 원본은 변경되지 않음

Object.assign을 이용한 복사

let car = {
  brand: "Toyota",
  model: "Camry"
};

let duplicate = Object.assign({}, car); // 빈 객체에 car의 모든 프로퍼티 복사
duplicate.brand = "Honda";

console.log(car.brand);     // "Toyota"
console.log(duplicate.brand); // "Honda"

여러 객체를 병합할 때도 사용할 수 있습니다.

let laptop = { brand: "Dell" };
let features1 = { hasTouchscreen: true };
let features2 = { hasBacklitKeyboard: true };

Object.assign(laptop, features1, features2);
console.log(laptop);
// { brand: "Dell", hasTouchscreen: true, hasBacklitKeyboard: true }

만약 앞 객체의 동일한 이름의 프로퍼티가 있다면, 뒤의 객체의 값으로 덮어씌워집니다.

let laptop = { brand: "Dell" };
let features1 = { hasTouchscreen: true };
let features2 = { brand: "Intel" };

Object.assign(laptop, features1, features2);
console.log(laptop);
// { brand: "Intel", hasTouchscreen: true }

깊은 복사

얕은 복사는 객체의 최상위 프로퍼티까지만 복사합니다. 즉 프로퍼티의 값이 또 다른 객체라면 그 내부 객체는 참조값만 복사되므로, 원본과 복제본이 같은 내부 객체를 공유하게 됩니다.

let team = {
  name: "Developers",
  members: {
    lead: "Alice",
    count: 5
  }
};

let copiedTeam = Object.assign({}, team);
console.log(team.members === copiedTeam.members);  // true, 같은 객체를 참조

// 한쪽에서 내부 객체 수정 시 모두에게 반영됨
team.members.count++;
console.log(copiedTeam.members.count);  // 6

깊은 복사를 하려면 내부의 모든 중첩 객체까지 새롭게 복제해야 합니다. 이를 위해서는 재귀적으로 복사하거나 lodash 라이브러리의 _.cloneDeep()같은 함수를 사용할 수 있습니다.

재귀적 복사

function deepClone(obj) {
  if (obj === null || typeof obj !== "object") return obj;

  let copy = Array.isArray(obj) ? [] : {};
  for (let key in obj) {
    copy[key] = deepClone(obj[key]);
  }
  return copy;
}

lodash 라이브러리

let deepClone = _.cloneDeep(user);