이 영역을 누르면 첫 페이지로 이동
lucid_dream 블로그의 첫 페이지로 이동

lucid_dream

페이지 맨 위로 올라가기

얕은 복사 & 깊은 복사

lucid_dream

얕은 복사 & 깊은 복사

  • 2021.10.03 00:40
  • 🌈 기술스택/JavaScript
글 작성자: NoHack
728x90

Data Types

자바스크립트의 데이터는 원시형(Primitive Type)과 참조형(Object Type)이라는 두 가지 종류의 타입으로 나뉩니다. 원시형에는 정수, 문자열, 심볼 등이 해당되고, 참조형에는 배열, 객체 등이 해당됩니다. 데이터 타입을 먼저 얘기하는 이유는 특정 변수의 데이터를 다른 변수에 복사하고자 할 때, 원본 변수가 담고 있는 데이터의 타입에 따라 복사 방식이 다르기 때문입니다.

각 방식을 얕은 복사(Shallow copy)와 깊은 복사(Deep Copy)라 말합니다. 👏

얕은 복사

다음 코드의 결과를 예상해 보세요.

let num1 = 123;
let num2 = num1;

console.log(num1 === num2);

num2 = 456;

console.log(num1 === num2);

const obj1 = { name: 'tom', age: 5 };
const obj2 = obj1;

console.log(obj1 === obj2);

obj2.name = 'jerry';

console.log(obj1 === obj2);

결과로 아마 [true, false, true, false]를 예상하셨을 텐데, 정답은 [true, false, true, true]입니다. 결과가 이렇게 나오는 이유는 원시형 데이터와 참조형 데이터가 메모리에 저장되는 방식이 서로 다르기 때문입니다. 위 예제에서 사용된 4개의 변수가 담긴 메모리 구조를 그림으로 간단하게 그려보면 다음과 같습니다.

그림에서 확인할 수 있는 것처럼 원시형 데이터는 스택 메모리만을 이용하고, 메모리에 값 자체를 저장합니다. 그래서 원본(num1)을 새로운 변수(num2)에 할당하면 값을 복사하기에 완전히 새로운 값이 만들어 집니다. 다만 비교 연산자는 123이라는 값 자체만 두고 비교하기 때문에, true라는 결과를 얻게 되는 것이지요.

반면 참조형 데이터는 스택 외에도 힙이라 하는 메모리를 사용하며, 이를 스택에서 참조하는 방식으로 동작합니다. 그렇기 때문에 스택에는 힙을 가리키는 주소를 담게 되고, 원본(obj1)을 새로운 변수(obj2)에 복사하게 되면 스택에 담긴 메모리 주소를 복사하게 됩니다. 그래서 두 변수는 동일한 하나의 객체를 가리키게 되고, obj2를 수정했음에도 obj1을 수정한 것과 같게 됩니다. 참조형 데이터의 특성으로 인해 메모리가 복사되는 것을 얕은 복사라 부릅니다.

원시형 데이터는 기본적으로 깊은 복사가, 참조형 데이터는 얕은 복사가 발생합니다.

깊은 복사

원시형 데이터와 달리 참조형 데이터는 얕은 복사가 발생하기 때문에 별도의 처리가 필요합니다. 깊은 복사를 위한 다양한 방법이 있지만, 본문에서는 대표적인 두 가지 방법에 대해 소개합니다.

반복문을 이용한 방법

const arr1 = [1, 2, 3, 4, 5];
const arr2 = [];

for (let i = 0; i < arr1.length; i++) {
  arr2.push(arr1[i]);
}
console.log(arr1 === arr2); // false;

const obj1 = { name: 'Tom', age: 5 };
const obj2 = {};

Object.keys(obj1).forEach((key) => {
  obj2[key] = obj1[key];
});
console.log(obj1 === obj2); // false;

반복문을 이용한 방법은 새로운 변수에 빈 배열([])과 빈 객체({})를 할당하면서 시작합니다. 할당 이후에는 반복문을 통해 복사하고자 하는 데이터의 값을 하나씩 복사하면 됩니다. 그렇게 해서 같은 값을 가졌지만, 서로 다른 변수가 됩니다.

전개 연산자를 이용한 방법

const arr1 = [1, 2, 3, 4, 5];
const arr2 = [...arr1];

console.log(arr1 === arr2); // false;

const obj1 = { name: 'Tom', age: 5 };
const obj2 = { ...obj1 };

console.log(obj1 === obj2); // false;

전개 연산자(Spread Operator)는 반복 가능한 요소(문자열, 배열, 객체)를 전개하여 배열 또는 객체로 만들어 줍니다. 내부의 값들을 전개하여 새로운 데이터를 만들기에 깊은 복사가 되며, 반복문을 이용한 방법보다 짧은 코드로 구현 가능합니다.

재귀 함수로 구현한 깊은 복사

위에서 소개한 두 가지 방법은 한 가지 아쉬운 점이 있습니다. 바로 최상위 블록(Level 1)에 대해서만 깊은 복사가 되고, 내부의 참조형 데이터들은 여전히 얕은 복사 된다는 점입니다. 따라서 이는 재귀 함수를 통해 구현해야 합니다.

const deepCopy = (param) => {
  if (typeof param !== 'object' || param === null) {
    return param;
  }

  if (param instanceof Array) {
    return param.reduce((newArr, val) => {
      newArr.push(deepCopy(val));
      return newArr;
    }, []);
  } else if (param instanceof Object) {
    return Object.keys(param).reduce((newObj, key) => {
      newObj[key] = deepCopy(param[key]);
      return newObj;
    }, {});
  }
};

const user = {
  champ: '이즈리얼',
  level: 1,
  stat: {
    ad: 50,
    ap: 10,
  },
  items: [
    { name: '롱소드', stack: 1 },
    { name: '체력 물약', stack: 3 },
  ],
};

console.log(user);
console.log(deepCopy(user));

중첩에 대해 깊은 복사를 하기 위해 이렇게 만들어도 좋지만, lodash 같은 라이브러리의 도움을 받아도 좋습니다. 하지만 깊은 복사만을 위해 라이브러리를 불러오는 것은 낭비라 생각되어, 그런 경우를 대비해 코드를 직접 구현할 수 있는 것도 중요하다 생각합니다. 😆

References

  • Spread syntax (MDN)
이 글은 본 저작자 표시, 비영리, 동일 조건 변경 허락 규칙 하에 배포할 수 있습니다. 자세한 내용은 Creative Commons 라이선스를 확인하세요.
본 저작자 표시
비영리
동일 조건 변경 허락

'🌈 기술스택 > JavaScript' 카테고리의 다른 글

자기 자신을 가리키는 값 this  (0) 2021.10.03
자바스크립트 코드의 유효 범위 Scope  (0) 2021.10.03
유사 배열 객체 만들기  (0) 2021.10.02
일급 객체 (First Class Object)  (0) 2021.10.02
호이스팅 (Hoisting)  (0) 2021.10.01

댓글

댓글을 사용할 수 없습니다.

이 글 공유하기

  • 구독하기

    구독하기

  • 카카오톡

    카카오톡

  • 라인

    라인

  • 트위터

    트위터

  • Facebook

    Facebook

  • 카카오스토리

    카카오스토리

  • 밴드

    밴드

  • 네이버 블로그

    네이버 블로그

  • Pocket

    Pocket

  • Evernote

    Evernote

다른 글

  • 자기 자신을 가리키는 값 this

    자기 자신을 가리키는 값 this

    2021.10.03
  • 자바스크립트 코드의 유효 범위 Scope

    자바스크립트 코드의 유효 범위 Scope

    2021.10.03
  • 유사 배열 객체 만들기

    유사 배열 객체 만들기

    2021.10.02
  • 일급 객체 (First Class Object)

    일급 객체 (First Class Object)

    2021.10.02
다른 글 더 둘러보기

정보

lucid_dream 블로그의 첫 페이지로 이동

lucid_dream

  • lucid_dream의 첫 페이지로 이동

검색

메뉴

  • All categories
  • About me
  • Guest Book

카테고리

  • 분류 전체보기 (122)
    • 💦 일상뻘글 (1)
    • ⭐️ 프로젝트 (7)
      • 사이드 프로젝트 (1)
      • 스터디 노트 (6)
    • 🌈 기술스택 (31)
      • Web Basic (10)
      • JavaScript (14)
      • React (0)
      • Git (7)
    • 💻 컴퓨터공학 (28)
      • 자료구조 (13)
      • 알고리즘 (7)
      • 운영체제 (4)
      • 소프트웨어 공학 (4)
    • 📝 문제풀이 (55)
      • 프로그래머스 (55)
      • 과제관 (0)
    • 🐹 취미생활 (0)
      • Film Log (0)
      • Cover Song (0)

정보

NoHack의 lucid_dream

lucid_dream

NoHack

나의 외부 링크

  • Github
  • Instagram

블로그 구독하기

  • 구독하기
  • RSS 피드

방문자

  • 전체 방문자
  • 오늘
  • 어제

티스토리

  • 티스토리 홈
  • 이 블로그 관리하기
  • 글쓰기
Powered by Tistory / Kakao. Copyright © NoHack.

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.