728x90

자바스크립트를 쓰는 개발자라면 매일 같이 마주하는

Object타입

이놈, 유용하지만 조심해서 써야한다.

 

코드를 쓰다보면 객체를 '복사'하게 되는 경우가 많은데 예를들면 아래와같다

const wallet = {
  card: 3,
  money: 0
};

const friend1_wallet = wallet;
const friend2_wallet = wallet;

console.log(wallet) // { card: 3, money: 0 }
console.log(friend1_wallet)  // { card: 3, money: 0 }
console.log(friend2_wallet)  // { card: 3, money: 0 }

 

자, 너무 나도 당연한 결과이다 그렇다면 이번엔 아래의 결과를 예상해보자

wallet.card = 4;

console.log(wallet);
console.log(friend1_wallet);
console.log(friend2_wallet);

 

아래의 결과가 정확히 예상되는가?

console.log(wallet); // { card: 4, money: 0 }
console.log(friend1_wallet); // { card: 4, money: 0 }
console.log(friend2_wallet); // { card: 4, money: 0 }

 

왜, 이런일이 생길까?

c와 같은 언어를 배우고 사용해본 개발자라면 메모리 참조에 대해서 알것이다.

const friend1_wallet = wallet; // 복사가 아닌 참조

 

friend1_wallet은 wallet이 가리키는 메모리 영역을 똑같이 바라보게 했을 뿐이기 때문에,

wallet을 바꾸면 당연하게도 firend1_wallet의 값이 바뀌게 되는것이다.

 

그렇다면, 

"지갑을 똑같이 복사한 다음 복사한 지갑에만 카드를 두개 더 넣어두고싶어요~"

라고 했을때 어떻게 처리를 해야할까?

 

우리의 javacript는 이를 위해 Object.assign() 메소드를 제공한다.

const wallet = {
  card: 3,
  money: 0
};

const friend1_wallet = Object.assign({}, wallet);
const friend2_wallet = Object.assign({}, wallet);

console.log(wallet) // { card: 3, money: 0 }
console.log(friend1_wallet)  // { card: 3, money: 0 }
console.log(friend2_wallet)  // { card: 3, money: 0 }

이렇게 하면 우리는 빈 객체에 wallet을 복사하게 된다.

 

따라서, 아래와 같이 실했했을때 결과를 예상해보자.

wallet.card = 6;

console.log(wallet);
console.log(freind1_wallet)
console.log(freind2_wallet)

 

결과를 예상해봤는가?

console.log(wallet); // { card: 6, money: 0 }
console.log(freind1_wallet); // { card: 3, money: 0 }
console.log(freind2_wallet); // { card: 3, money: 0 }

 

자, 이제 목적을 달성했다.

그런데...

"저는 지갑에 어머니가 맡기신 돈도 보관해야되요. 돈을 두가지로 분류해서 저장해주세요!" 라고 한다면

const wallet = {
  card: 3,
  money: {
    mother: 5000,
    mine: 100,
  },
};

const freind1_wallet = Object.assign({}, wallet);
const freind2_wallet = Object.assign({}, wallet);

console.log(wallet); // { card: 3, { mother: 5000, mine: 100 } }
console.log(freind1_wallet); // { card: 3, { mother: 5000, mine: 100 } }
console.log(freind2_wallet); // { card: 3, { mother: 5000, mine: 100 } }

 

자... 이런 상황에서 엄마가 1000원을 더 맡기셔서 wallet에 돈을 추가하는 상황이 되었다.

아래 상황의 결과를 예측해보자

wallet.money.mother += 3000;

console.log(wallet);
console.log(friend1_wallet);
console.log(friend2_wallet);

 

결과는 아래와 같다.

console.log(wallet); // { card: 3, money: { mother: 8000, mine: 100 } }
console.log(friend1_wallet); // { card: 3, money: { mother: 8000, mine: 100 } }
console.log(friend2_wallet); // { card: 3, money: { mother: 8000, mine: 100 } }

 

어라! 복사하면 참조 문제는 발생하지 않는것 아니었나요?

자, 여기서 이제 "얕은 복사" 라는 개념이 나온다.

Object.assign()은 얕은 복사를 해주는 메소드로 1Depth 까지만 그 특성을 유지해준다.

 

전개연산자도 이와 마찬가지다.

// 전개연산자 얕은복사 예시
const wallet = {
  card: 3,
  money: 0
};

const freind1_wallet = { ...wallet };

console.log(wallet); // { card: 3, money: 0 }
console.log(freind1_wallet); // { card: 3, money: 0 }

wallet.card = 4;

console.log(wallet); // { card: 4, money: 0 }
console.log(freind1_wallet); // { card: 3, money: 0 }


// 전개연산자 깊은복사...
const wallet = {
  card: 3,
  money: {
    mother: 5000,
    mine: 100
  }
};

const freind1_wallet = { ...wallet };

console.log(wallet); // { card: 3, money: { mother: 5000, mine: 100 } }
console.log(freind1_wallet); // { card: 3, money: { mother: 5000, mine: 100 } }

wallet.money.mother += 3000;

console.log(wallet); // { card: 3, money: { mother: 8000, mine: 100 } }
console.log(freind1_wallet); // { card: 3, money: { mother: 8000, mine: 100 } }

 

이런 문제를 해결하려면 어떻게해야 할까?

늘 그렇듯 방법이 있다.

1. 재귀함수를 통한 복사

2. JSON 메소드 응용하기

3. lodash등 라이브러리르 이용하기

 

1. 재귀함수를 통한 복사는 빈 객체를 생성하고 for...in 함수로 객체의 key,value를 빈 객체에 집어넣는 방식이다.

프로젝트 내에 deepCopy함수를 하나 생성해두고 두고두고 쓰면 제법 유용하다.

function deepCopy(obj) {
  const newObj = {};

  for (let key in obj) {
    if (typeof obj[key] === 'object') {
      newObj[key] = deepCopy(obj[key]);
    } else {
      newObj[key] = obj[key];
    }
  }

  return newObj;
}

 

2. JSON 메서드를 응용하는 방법은 간편하지만 퍼포먼스 적으로 좋지않다고 한다. 또한, File 타입 복사간 문제가 발생하니 사용을 지양하는게 좋을것으로 생각된다.

const obj = {
  a: 1,
  b: {
    b_1: 2,
    b_2: 3,
  }
}

const copiedObj = JSON.parse(JSON.stringify(obj));

 

3.  lodash 등 라이브러리 사용하기는 쉽고 강력하므로 개인적으로 이 방식을 권장하는바이다.

import _ from 'lodash';

const obj = {
  a: 1,
  b: {
    b_1: 2,
    b_2: 3,
  }
}

const copiedObj = _.cloneDeep(obj);
728x90
반응형