본문 바로가기
JavaScript

2021.04.22 JavaScript Object 복사

by 해맑은 코린이 2021. 4. 22.

2021.04.22_JavaScript Object 복사_정리노트

 

 오늘은 많이는 안쓰이지만, 단순히 따라하는 방법이 나한테는 어려워서 따로 정리 ㅎㅁㅎ

또한 객체의 중요한 포인트를 짚고 넘어가고 싶어서 포스팅 쓰기!!

 

오늘도 역시 멋진 모던 자바스크립트로 공부하기.

ko.javascript.info/object-copy

 

 


 

 

객체는 참조에 의해 저장되고, 복사된다.

원시값은 값자체가 복사 되지만, 객체는 참조에 의해 저장되고 복사된다.

 

이게 머선 말이냐, 원시값은 a=b 라고 가정했을때, b에서 a의 값 자체가 복사가 되는데, 

객체는 objA = objB 라고 복사를 하고 싶으면, objA 의 값이 objB 자체에 복사가 되는 것이 아니라, 실제 객체의 값이 들어 있는 메모리 주소인 객체에 대한 참조 값저장된다는 것이다.

 

풀어 설명하면,

let objA = { 'name' : 'Korin' } ;

let objB = objA ;    // objB 는 참조값을 복사함.
                     // objA 와 objB 는 각각 동일 객체에 대한 참조 값이 저장됨.
                     
                     
//objA 의 객체의 값은 메모리 내에 어딘가에 저장되고, // 변수 objA // 에는 객체를 '참조' 할 수 있는 값이 저장됨.
// >> 객체가 할당된 변수를 복사할 땐 객체의 참조 값이 복사되고 객체의 값 자체는 복사되지 않음.

 

이를 문서에 적힌대로, 서랍장에 비유를 하면, 

 

우리가 서랍장에 객체 값을 넣어두고, 자물쇠로 잠금했다고 가정하면, 변수를 2개 선언하고 변수에 해당 객체를 할당했다면, 한 서랍장에 있는 객체 값을 꺼낼 수 있는 열쇠가 2개 ( objA, objB ) 가 생기는 셈이다. 값이 들어 있는 서랍장은 한개, 열쇠는 2개라고 비유하면 이해가 쉬울 것 같다! 

 

 

그렇기 때문에, 객체에 접근할 때, objB 에서 참조값으로 객체의 값을 변경하면, objA 로 접근할 때도 참조값을 사용해서 변경사항을 확인해서, objA 의 값 또한 변경 된다. 

 

let objA = { 'name' : 'Korin' } 
let objB = objA ;             // 참조값 복사

objB.name = 'korinNote' ; 

console.log(objA.name)       // 콘솔창에 korinNote 출력.

 

 

 


 

참조에 의한 객체의 비교를 할 때, == ( 동등 연산자 ) , === ( 삼항 연산자 ) 는 동일하게 작동된다.

 

let a = {};
let b = a;               // 참조에 의한 복사

console.log ( a== b ) ;  // true

console.log ( a === b ); // true

 

 

하지만, a와 b를 각각 빈 객체로 선언한 경우, 이는 다른 독립된 객체이다.

let a = {};
let b = {};             

console.log ( a== b ) ;  // false

console.log ( a === b ); // false



// 원시 값인 경우, 이 둘은 같다고 봄.

let c = 0;
let d = 0;

console.log ( c== d ) ;  // true

console.log ( c === d ); // true

 

 

이렇게 객체와 원시값의 차이를 알 수 있다.

 

 

그렇다면, 기존에 있던 객체를 똑같이 복사하면서, 독립된 객체를 만들고 싶다면 ? 

 

이는 자바스크립트의 내장 메서드가 없어서 방법은 있지만 어렵다. 객체를 복사해야 할 일은 거의 없고, 거의 참조에 의한 복사로 해결 가능한 일이 대다수이다. 하지만 그래도 배워놓고 가보자!! 

 

객체를 복사하려면, 반복문을 사용해서, 값 하나하나를 복사해서 새로운 오브젝트에 넣는 것인데, 반복문을 사용하지 않고, 넣을 수 있는 방법이 있다. 

 


 

Object.assign

이는 문서에서 간단히 설명되어 있어서 MDN 에서 내 맛대로 추가로 뽑아 왔다 ㅎㅁㅎ. 

 

일단 동작방법!

 

 

Object.assign(target, ...sources)

 

target 은 목표로 하는 객체, sources 는 복사하려는 값 ( 여러 개 와도 상관 없음 ) 

 

String 과 Symbol 속성 모두가 복사됨. 

 

 

일단 이정도로 해두고, 예시를 들면서 ㄱㄱ 

 

 

간단하게 첫번째 예시로, 빈 객체에 하나의 객체의 값을 복사해봅씨다.

 

let user = {
   'name': "Korin",
    'age': 23,
   };
  
  // 빈 객체를 생성함과 동시에, user 의 값을 복사
  let clone = Object.assign({}, user);
  
  console.log (user) ;
  
  console.log (clone) ; 

 

 

 

이렇게 clone 변수에 user 의 값이 그대로 저장된다.

 

 

 

하지만 둘을 비교해보면, false 를 반환하므로, 둘은 같은 값을 가진 독립된 객체라 할 수 있는 것!

 

 

 

또 동일한 키가 존재할 경우를 복사하게 되면, 타겟으로 쓰인 객체는 소스의 객체의 속성으로 덮여쓰여지고, 그 후에 소스의 객체 또한 타겟 객체와 유사하게 덮어쓰인다.

//동일한 키가 존재할 경우 타겟 객체의 속성은 소스 객체의 속성으로 덮어쓰여짐.
// 그 후 소스로 쓰인 객체 또한 타겟의 객체와 유사하게 덮어쓰여짐.



const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };

const cloneTarget = Object.assign(target, source);


// b 의 값이 덮어씌워짐.

console.log(target);
//  { a: 1, b: 4, c: 5 }

// 복사된 객체도 똑같이 적용됨.

console.log(cloneTarget);
//  { a: 1, b: 4, c: 5 }

 

 

넘무 헷갈려서 일부러 한글 = 영어 통일해 보았음...  찬찬히 생각해보면, 이해할 수 있으니 나도 적어놓고 다음에 헷갈릴 때 또 봐야지 ㅎㅁㅎ

 

 

쨋든 역시 이거 또한, 같은 값을 가지면서 서로 독립된 객체이다.

 

 

또한 , 속성은 파라미터 순서에서 더 뒤에 위치한 동일한 속성을 가진 다른 객체에 의해 덮어쓰인다. 

 

const o1 = { a: 1, b: 1, c: 1 };
const o2 = { b: 2, c: 2 };
const o3 = { c: 3 };

const obj = Object.assign({}, o1, o2, o3);


// b 는 b가 제일 뒤에 있는 o2 참조 , c는 c 가 제일 뒤에 있는 o3 참조

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

 

 

* 주의 : Object.assign() 메소드는 null 또는 undefined 출처 값에 대해서는 오류를 던지지 않음.

 

let v1 = 'abc';


let obj = Object.assign({}, v1, null, undefined);

// 원시 자료형은 래핑되지만, null 과 undefined 는 무시됨.
// 문자열은 자체적으로 열거형 속성을 가진다.

console.log(obj); // { "0": "a", "1": "b", "2": "c" }

 

여기까지 내가 지금 현재 배운 단계에서 MDN 을 사용해서 요약해보았음! 어렵군.. 또한 다른 원시형 타입등을 복사하려면 다른 메소드를 써야하기에 오늘은 여기까지!

 

 


 

++ ES9 ( 2018 ) 에서는 ... 를 사용하여 복사를 좀 더 간단하게 구현할 수 있다.


 let obj = { a: 1, b: 2 };

 let cloneObj = { ...obj };

 console.log(obj === cloneObj) // false
 

 

 

 


 

마지막으로 하나만 더 알아보고 정리 !

 

여기까지는, 모든 프로퍼티 , 즉 키의 값들이 원시형인 값만 예시를 들었는데, 만약 객체안에 프로퍼티로 객체가 존재하는 중첩 객체에 대한 복사는 어떻게 되는 걸까?

 

 

이를 우리는 깊은 복사라 한다.

깊은 복사를 하는 방법은 일반 복사와는 다르기 때문에, 이 방법 또한 정리 !

 

예시를 들어볼까 ? 

 

let user = {
    name: "Korin",
    sizes: {
      height: 200,
      width: 100,
    }
   };

let clone = Object.assign({}, user);

console.log ( user ) ;
console.log ( clone ) ;

console.log( user.sizes === clone.sizes ); // true 둘은 같은 객체다.

 

 

 

콘솔창에 보이듯이, clone에 user 가 잘 복사되었다. 하지만, 이 둘안에 있는 sizes 를 보면 같은 객체라고 나온다. 

 

이가 문제가 될 수 있는 경우는 이거다.

  // user와 clone는 sizes를 공유.
  
  user.sizes.width++;       // 한 객체에서 프로퍼티를 변경.
  console.log(user.sizes.width); //  101
  console.log(clone.sizes.width); // 101, 다른 객체에서 변경 사항을 확인할 수 있음.

 

이렇게 user 에서 sizes 의 값을 변경하면, clone 의 sizes 또한 바뀌게 된다.  

obj = { a: 1, b: 2, c: { aa: 3 } };

cloneObj = { ...obj };

console.log(obj === cloneObj)      // false 
console.log(obj.c === cloneObj.c ) // true 

 

또한 위에서 설명한 ... 의 경우도 깊은 복사는 불가하다.

 

그러면 진짜 깊은 복사본의 객체를 만들려면 무슨 방법이 있을까?

 

가장 대표적인 방법은 자바스크립트 라이브러리인 lodash 사용이다. 

 

lodash 라이브러리는 자바스크립트에서 가장 흔히 쓰이는 라이브러리로, 나중에 여러 기능들을 배우고 나서 쓰기에 유용해서 하나 가져와봤다.

 

 

먼저 해당 라이브러리를 쓰기 위해서,

<!-- jsdelivr.net cdn 링크 복붙 -->
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>

 

html 태그에 해당 cdn 링크를 복붙해준다.

 

 

 

jsdelivr.net 은 여러 오픈소스 cdn 링크가 많은 곳이니 기억해두시길. 쨋든 여기서 npm 을 클릭후, lodash 를 검색해서 들어가서 해당 링크를 복사한 것이다. 

 

 

우리는 해당 라이브러리에서 _.cloneDeep 메소드를 사용할 것이다. 

 

이 메소드는 객체의 깊은 복사를 지원한다. 

 

출처 : lodash 공식문서

 

let user = {
  name: "Korin",
  sizes: {
    height: 200,
    width: 100,
  }
};

// cloneObj 에 user 을 값을 깊은 복사함.
cloneObj = _.cloneDeep(user);

//둘은 같은 값을 가지고, 깊은 복사를 하게 됨.
console.log(user);
console.log(cloneObj);

// 둘은 독립된 개체
console.log (user === cloneObj ) ;          //false

console.log(user.sizes === cloneObj.sizes); //false

 

이렇게 하면, 

 

 

 

둘은 같은 값을 복사함과 동시에 , 독립된 개체가 되어, 만약 한 객체에서 값을 수정한다면, 그것은 독립된 개체이므로 서로 다른 값을 가지게 된다.

 

 

user.sizes.width++;
console.log(user.sizes.width);       // 101
console.log(cloneObj.sizes.width);   // 100

 

 

이렇게!!!!

 

 


 

후... 오늘도 혼자 이리저리 또 나름 깊게깊게 또 들어가서 예를 많이 들다 보니 포스팅이 길어졌군...^^

 

요즘엔 개념에 대한것만 포스팅 하다보니,,,, 슬슬 지루할 때가 되었으니 또 프로젝트나 혼자 우당탕탕 해볼까 생각중 ㅎㅁㅎ

하지만 그만큼 자바스크립트에서 많이 쓰이는 객체에 대해서 좀 깊게 알게 된 것 같아 아직 한참 멀었다는 자괴감과 함께 그래도 조금씩 다시 포스팅을 쓰던 때로 돌아가자 하면서 찬찬히 하는중이다! 그렇다면 오늘은 길고 길었던 객체 복사 끝!

댓글