IT의 IT 블로그

얕은 복사 / 깊은 복사 정리 본문

Frontend Basic (기초 이론)

얕은 복사 / 깊은 복사 정리

IT의 IT 블로그 2026. 2. 9. 17:30

안녕하세요.
오늘은 자바스크립트에서 자주 등장하지만
유독 헷갈리기 쉬운 개념인 얕은 복사(Shallow Copy)깊은 복사(Deep Copy) 를 정리해보려고 합니다.

이 주제를 정리하게 된 이유는 단순합니다.

그래서 이 글에서는
“왜 문제가 생기고, 그래서 언제 무엇을 써야 하는지”
이 관점에 집중해서 정리해보겠습니다.


1. 이 문제의 출발점

1.1 우리가 자주 겪는 상황

자바스크립트를 사용하다 보면
객체를 하나 만들어 두고,
다른 변수에 그대로 할당해서 사용해 본 경험이 있을 겁니다.

 
const a = { age: 20 };
const b = a;

b.age = 30;

// a.age도 30으로 변함

 

“분명히 b만 수정했는데
왜 a도 같이 바뀌지?”

 

버그처럼 느껴질 수도 있지만,
사실 이 코드는 얕은 복사나 깊은 복사 이전 단계의 문제입니다.

이 코드는 객체를 복사한 것이 아니라,
같은 객체를 가리키는 변수를 하나 더 만든 것입니다.

이 차이를 이해하지 못하면
얕은 복사와 깊은 복사도 계속 헷갈릴 수밖에 없습니다.

그래서 이 글에서는
먼저 복사가 아닌 경우 부터 짚고,
그 다음에 얕은 복사와 깊은 복사를 살펴보겠습니다.


1.2 이 문제를 이해하는 기준

복잡한 설명보다
먼저 기준 하나만 잡고 가겠습니다.

자바스크립트에서 값이 대입될 때,

  • 값 자체가 복사된 건지
  • 참조(주소)가 복사된 건지

이 차이만 이해하면
얕은 복사와 깊은 복사의 대부분은 자연스럽게 설명됩니다.


2. 자바스크립트의 값은 딱 두 종류다

자바스크립트의 모든 값은
아래 두 가지 중 하나입니다.

2.1 원시값 (Primitive)

  • number
  • string
  • boolean
  • null, undefined
  • symbol, bigint

원시값의 특징은 단순합니다.

값 그 자체를 가진다


2.2 객체 (Object)

  • 객체 {}
  • 배열 []
  • 함수 function
  • Date, Map, Set 등

객체의 특징은 하나입니다.

값이 아니라, 값이 있는 위치(참조)를 가진다

이 차이가
얕은 복사와 깊은 복사의 모든 출발점입니다.


3. 원시값은 어떻게 복사될까?

3.1 원시값 대입 예제

 
let a = 10;
let b = a;

b = 20;

 

결과는 당연히 다음과 같습니다.

a // 10
b // 20

 

 

이유는 간단합니다.

  • 10은 원시값이고
  • 대입 시 값 자체가 복사되기 때문입니다.

그래서 한쪽을 바꿔도
다른 쪽에는 전혀 영향이 없습니다.


3.2 객체 안의 원시값도 동일하다

 
const obj1 = { age: 20 };
const obj2 = { ...obj1 };

obj2.age = 30;

결과는 다음과 같습니다.

obj1.age // 20
obj2.age // 30

 

이 경우에도 문제는 없습니다.
age는 원시값이기 때문입니다.


4. 객체는 왜 문제가 될까?

4.1 객체 대입은 복사가 아니다

 
const obj1 = { age: 20 };
const obj2 = obj1;

obj2.age = 30;

결과는 다음과 같습니다.

 
obj1.age // 30

 

이유는 단순합니다.

객체 변수에는
객체 자체가 아니라
객체를 가리키는 참조(주소) 가 들어 있습니다.

즉,

const obj2 = obj1;

 

이 코드는
객체를 복사한 것이 아니라,
같은 객체를 가리키는 변수 하나를 더 만든 것입니다.

그래서 하나를 수정하면
다른 쪽도 함께 바뀝니다.


4.2 이건 얕은 복사도 아니다

여기서 많이 헷갈리는 부분이 있습니다.

 
const b = a;

이 코드는

  • 얕은 복사 ❌
  • 깊은 복사 ❌

입니다.

복사 자체가 아니라 단순한 참조 대입입니다.


5. 얕은 복사 (Shallow Copy)

5.1 얕은 복사란?

겉(1단계)만 새로 만들고,
내부에 있는 객체는 그대로 공유하는 복사


5.2 얕은 복사 예제

 
const original = {
  name: "kim",
  info: { age: 20 }
};

const copy = { ...original };

원시값 변경

copy.name = "lee";

original.name // "kim"
copy.name     // "lee"

문제 없습니다.

객체 내부 값 변경

copy.info.age = 30;

original.info.age // 30

문제가 발생합니다.


5.3 왜 이런 일이 생길까?

얕은 복사는 객체의 겉 구조만 새로 만들고,
그 안에 들어 있는 값은 타입에 따라 다르게 복사됩니다.

  • name → 원시값 → 값 복사
  • info → 객체 → 참조 복사

즉, 실제 구조는 다음과 같습니다.

const original = {
  name: "kim",
  info: { age: 20 }
};

const copy = { ...original };
[original 객체]                         [info 객체(중첩)]
original ──▶ ┌───────────────────┐
              │ name : "kim"      │
              │ info : (참조) ────┼──────────▶ ┌──────────┐
              └───────────────────┘             │ age : 20 │
                                                └──────────┘
                                                   ▲
[copy 객체]			                   │
copy ─────▶  ┌───────────────────┐                │
              │ name : "kim"      │                │
              │ info : (참조) ────┘────────────────┘
              └───────────────────┘
 

`original`과 `copy`는 서로 다른 객체지만,
`info` 프로퍼티는 같은 객체를 함께 가리키고 있습니다.

그래서 얕은 복사 이후에
중첩된 객체를 수정하면
원본도 함께 변경되는 문제가 발생합니다.

이런 상태를 얕은 복사(Shallow Copy) 라고 부릅니다.


6. 얕은 복사는 언제 써도 될까?

6.1 써도 되는 경우

  • 중첩 객체가 없을 때
  • 중첩 객체를 읽기만 할 때
  • React 상태 업데이트처럼
    변경되는 경로만 새로 만들 때

6.2 위험한 경우

  • 중첩 객체를 직접 수정할 때
  • 설정값, 보안 정보처럼
    원본이 바뀌면 안 되는 데이터

7. 깊은 복사 (Deep Copy)

7.1 깊은 복사란?

중첩된 객체까지 전부 새로 만들어
원본과 완전히 분리하는 복사


7.2 깊은 복사 예제

 
const original = {
  name: "kim",
  info: { age: 20 }
};

const copy = structuredClone(original);

copy.info.age = 30;

결과는 다음과 같습니다.

 
original.info.age // 20
copy.info.age     // 30

이제 원본과 복사본은
완전히 독립입니다.


8. 얕은 복사 vs 깊은 복사 정리

 

구분 얕은 복사 깊은 복사
복사 범위 1단계만 중첩까지 전부
객체 공유 있음 없음
원본 보호
사용 용도 상태 업데이트 스냅샷, 완전 복제

 

9. 얕은 복사와 깊은 복사의 다양한 활용 방식

얕은 복사와 깊은 복사는
어느 한쪽이 항상 정답인 개념은 아닙니다.

상황에 따라
공유가 필요한지, 분리가 필요한지에 따라
적절한 방식을 선택해서 사용하게 됩니다.


9.1 부분 수정이 필요한 경우 (얕은 복사)

객체 전체를 바꿀 필요 없이
일부 값만 변경해야 하는 경우에는
얕은 복사가 적합합니다.

const nextState = {
  ...state,
  user: {
    ...state.user,
    age: 30
  }
};
 

이 방식은

  • 바뀌는 부분만 새로 만들고
  • 나머지 구조는 그대로 유지하기 때문에

불필요한 복사를 줄일 수 있습니다.

9.2 원본을 보호해야 하는 경우 (깊은 복사)

설정값이나 기준 데이터처럼
원본이 변경되면 안 되는 경우에는
깊은 복사가 필요합니다.

const copiedConfig = structuredClone(config);

 

이렇게 복사해 두면
복사본을 자유롭게 수정해도
원본에는 전혀 영향을 주지 않습니다.

9.3 임시 작업용 데이터가 필요한 경우

데이터를 가공하거나 변환하는 과정에서
원본을 건드리지 않고
실험적으로 수정해 보고 싶을 때가 있습니다.

이럴 때는
작업 범위에 따라 얕은 복사 또는 깊은 복사를 선택합니다.

  • 단순 값 변경 → 얕은 복사
  • 구조 변경이나 중첩 수정 → 깊은 복사

9.4 비교·되돌리기(Undo)를 위한 스냅샷

이전 상태로 되돌려야 하거나,
변경 전·후 상태를 비교해야 하는 경우에는
깊은 복사가 적합합니다.

const snapshot = structuredClone(state);

 

이렇게 저장해 둔 스냅샷은
이후 상태 변경과 완전히 분리되어 안전하게 사용할 수 있습니다.

9.5 성능을 고려해야 하는 경우

모든 상황에서 깊은 복사를 사용하면
데이터 크기에 따라 성능 부담이 커질 수 있습니다.

그래서 실무에서는

  • 공유해도 괜찮은 부분은 공유하고
  • 분리가 필요한 부분만 새로 만드는 방식으로

성능과 안정성의 균형을 맞춥니다.


10. 이 글의 핵심 문장

이 글을 한 문장으로 정리하면 다음과 같습니다.

원시값은 대입 시 값이 복사된다.
객체는 대입 시 참조가 복사되어 같은 객체를 가리킨다.
객체를 완전히 분리하려면 중첩 객체까지 새로 만드는 깊은 복사가 필요하다.


11. 마무리

얕은 복사와 깊은 복사는
어려운 이론적 개념이라기보다
버그를 피하기 위한 판단 기준에 가깝습니다.

  • 왜 값이 같이 바뀌는지
  • 언제 공유되고
  • 언제 분리해야 하는지

이 세 가지만 판단할 수 있으면 충분합니다.

 

이상으로 정리를 마칩니다.
이 글이 개념을 정리하는 데 도움이 되었기를 바랍니다.
읽어주셔서 감사합니다.