JavaScript 얕은 복사와 깊은 복사

Youngjin Son·2023년 4월 27일

JavaScript

목록 보기
2/4
post-thumbnail

1. 원시 자료형의 복사

원시 자료형을 할당한 변수를 다른 변수에 할당하면 값 자체의 복사가 일어난다.
값 자체가 복사된다는 것은 하나의 값을 변경해도 다른 하나에 영향을 미치지 않는다는 것이다.

let n1 = 1;
let n2 = n1;
console.log(n1); // 1
console.log(n2); // 1
console.log(n1 === n2) //true

n2 = 2;
console.log(n1); // 1
console.log(n2); // 2
console.log(n1 === n2); //false

2. 참조 자료형의 복사

참조 자료형은 임의의 저장공간(heap)에 값을 저장하고 그 저장공간을 참조하는 주소를 메모리에 저장한다.
따라서 다른 변수에 할당할 경우 값 자체가 아닌 메모리에 저장되어 있는 주소가 복사된다.

let arr1 = [1, 2, 3];
let arr2 = arr;

console.log(arr1); // [1, 2, 3]
console.log(arr2); // [1, 2, 3]
console.log(arr1 === arr2); //true

arr2.push(4);
console.log(arr1); // [1, 2, 3, 4]
console.log(arr2); // [1, 2, 3, 4]
console.log(arr1 === arr2); // true

참조 자료형이 저장된 변수를 다른 변수에 할당할 경우, 두 변수는 같은 주소를 참조하기 때문에 하나의 값을 바꾸면 다른 하나도 영향을 받는다.


3. 배열 복사하기

배열을 복사하는 방법은 배열 내장 메소드인 slice()를 이용하는 방법과 spread 문법을 사용하는 방법이 있다.

(1) slice()

let arr1 = [1, 2, 3];
let arr2 = arr1.slice();
console.log(arr1); // [1, 2, 3]
console.log(arr2); // [1, 2, 3]
console.log(arr1 === arr2); // false

참조하고 있는 주소가 다르기 때문에 false가 나온다.

arr2.push(4)
console.log(arr1); // [1, 2, 3]
console.log(arr2); // [1, 2, 3, 4]

주소가 다르기 때문에 하나의 배열에 요소를 추가해도 다른 배열에 영향을 미치지 않는다.

(2) spread 문법

spread 문법은 ES6에서 새롭게 추가된 문법으로, 배열을 펼칠 수 있다.

let arr1 = [1, 2, 3];
let arr2 = [...arr1];
console.log(arr2); // [1, 2, 3]
console.log(arr1 === arr2); //false

arr2.push(4);
console.log(arr1); // [1, 2, 3]
console.log(arr2); // [1, 2, 3, 4]

4. 객체 복사하기

객체를 복사할 때는 Object.assgin()을 사용하는 방법과 spread 문법을 사용하는 방법이 있다.

(1) Object.assign()

let obj1 = { 
  name: 'Bob',
  age: 30
};
let obj2 = Object.assign({}, obj1);

console.log(obj2); // { name: 'Bob', age: 30 }
console.log(obj1 === obj2); //false

(2) spread 문법

let obj1 = {
  name: 'Bob',
  age: 30
};
let obj2 = {...obj1};
console.log(obj2); // { name: 'Bob', age: 30 }
console.log(obj1 === obj2); // false

5. 얕은 복사

참조 자료형을 복사할 때에는 예외 상황이 있다.
참조 자료형 내부에 참조 자료형이 중첩된 경우, 중첩된 구조는 복사할 수 없다.
참조 자료형이 몇 단계로 중첩되어 있던지 위에서 배운 slice(), Object.assign(), spread 방법으로는 중첩된 구조의 한 단계까지만 복사할 수 있다.

let users = [
  {
    name: 'Bob',
    age: 30
  },
  {
    name: 'Jain',
    age: 35
  },
  {
    name: 'Jhon',
    age: 40
  }
];

let copied = users.slice();
console.log(users === copied); // false

userscopied는 각각 다른 주소를 참조하기 때문에 false가 반환된다.

console.log(users[0] === copied[0]); // true

하지만 userscopied의 0번째 요소를 비교하면 true가 반환된다.
각 요소는 여전히 같은 주소값을 참조하기 때문이다.
이처럼 slice(), Object.assign(), spread 등의 방법으로 참조 자료형을 복사하면 중첩된 구조의 한 단계까지만 복사한다.
이를 얕은 복사(shallow copy) 라고 한다.


6. 깊은 복사

얕은 복사와 달리 참조 자료형 내부에 중첩된 모든 참조 자료형을 복사하는 것을 깊은 복사 라고 한다.
JavaScript 내부적으로는 깊은 복사를 수행할 수 있는 방법이 없기 때문에 JavaScript의 다른 문법을 응용하여 깊은 복사를 수행 할 수 있다.

(1) JSON.stringfy()와 JSON.parse()

JSON.stringfy() 는 참조 자료형을 문자열 형태로 변환하여 반환한다.
JSON.parse() 는 문자열의 형태를 객체로 변환하여 반환한다.
중첩된 참조 자료형을 JSON.stringfy()를 사용해 문자열 형태로 반환하고, 반환된 값에 JSON.parse() 를 사용하면 깊은 복사와 같은 결과를 반환할 수 있다.

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

console.log(arr); // [1, 2, [3, 4]]
console.log(copied); // [1, 2, [3, 4]]
console.log(arr === copied); // false
console.log(arr[1] === copied[1]); // false

이 방법으로 깊은 복사를 할 수 있어 보이지만, 이 경우에도 예외가 있다.
중첩된 자료형 중 함수가 포함된 경우 위 방법을 사용하면 함수가 null로 바뀌게 된다.
따라서 완전한 깊은 복사 방법이라고 보기는 어렵다.

(2) 외부 라이브러리 사용

반드시 완전한 깊은 복사를 해야 하는 경우라면 node.js 환경에서 외부 라이브러리인 lodash 또는 ramda 를 설치하면 된다.
lodashramda 는 각각의 방법으로 깊은 복사를 구현해 두었다.

// lodash를 사용한 예시
const lodash = require('lodash');

const arr = [1, 2, [3, 4]];
const copied = lodash.cloneDeep(arr);

console.log(arr); // [1, 2, [3, 4]]
console.log(copied); // [1, 2, [3, 4]]
console.log(arr === copied); // false
console.log(arr[1] === copied[1]); // false

7. SUMMARY

  • 원시 자료형이 할당된 변수를 다른 변수에 할당하면 값 자체의 복사가 일어나기 때문에 하나를 변경해도 다른 하나에 영향을 미치지 않는다.
  • 참조 자료형이 할당된 변수를 다른 변수에 할당하면 주소가 복사되기 때문에 하나를 변경하면 다른 하나에 영향을 미친다.
  • 배열을 복사할 때는 slice() 또는 spread 문법으로 복사할 수 있다.
  • 객체를 복사할 때는 Object.assign() 또는 spread 문법으로 복사할 수 있다.
  • 위 방법으로 참조 자료형을 복사하면 중첩된 구조의 한 단계까지만 복사되는 얕은 복사 가 일어난다.
  • JavaScript 내부적으로는 깊은 복사를 할 방법이 없지만 다른 문법을 응용할 수 있다.
  • JSON.stringfy(), JSON.parse()를 사용하는 방법이 있지만 참조 자료형 내부에 함수가 중첩된 경우 예외가 발생한다.
  • 완전한 깊은 복사를 위해서는 node.js 환경에서 외부 라이브러리인 lodash 또는 ramda 를 사용할 수 있다.

0개의 댓글