
자바스크립트에서 객체와 배열을 복때는 얕은 복사(Shallow Copy)와 깊은 복사(Deep Copy)라는 개념이 있어요. 복사 방식과 값의 타입에 따라 원본 데이터에 영향을 줄 수도 있기 때문에 두 개념의 차이와 예시를 정리해보았습니다.
자바스크립트에서 복사는 변수나 객체를 새로 만들 때 기존 데이터를 그대로 가져오는 과정을 의미해요. 복사는 크게 두 가지로 나눌 수 있습니다.
값 복사 (Primitive Value Copy) 데이터 자체를 복사합니다. 복사본과 원본이 완전히 독립적입니다.
참조 복사 (Reference Copy) 데이터의 메모리 주소를 복사합니다. 복사본과 원본이 같은 객체를 바라보게 되어, 복사본을 수정하면 원본에도 영향을 줄 수 있습니다.
복사 방식에 따라 얕은 복사(Shallow Copy) 또는 깊은 복사(Deep Copy)가 결정되며 복사된 값의 타입에 따라 원본 데이터에 영향을 주는지 여부가 달라집니다.
값 타입 (number, string, boolean, undefined, null, symbol, bigint) 복사 시 값 자체가 복사되어 원본과 복사본이 완전히 독립적입니다.
참조 타입 (object, array, function) 복사 시 주소가 복사되어 원본과 복사본이 같은 객체를 참조할 수 있습니다.
💡 값 타입 vs 참조 타입의 저장 방식 차이
값 타입 (Primitive Type)값 자체가 메모리에 저장돼요.let a = 10; let b = a; // 10이 복사됨 b = 20; console.log(a); // 10 (원본 그대로)✔️ b는 a의 값을 복사했을 뿐 a와 b는 서로 전혀 다른 메모리 공간을 사용해요.
참조 타입 (Reference Type)값은 메모리에 저장돼 있고, 변수에는 그 값이 저장된 '주소'가 저장돼요.let a = { price: 1000 }; let b = a; // '주소'가 복사됨 b.price = 500; console.log(a.price); // 500 (원본도 같이 변경됨)✔️ a와 b는 같은 주소를 바라보고 있기 때문에 b를 수정하면 a도 같이 바뀌어요.
자바스크립트는 값 타입은 메모리 크기가 작아 값 자체를 복사하고, 참조 타입은 데이터 크기가 커질 수 있어 주소만 복사하는 방식으로 메모리를 효율적으로 관리해요
얕은 복사는 객체나 배열을 복사할 때 첫번째 단계(depth)만 복사해서 원본과 복사본이 내부적으로 같은 참조(reference)를 공유하게 돼요.
🤔내부적으로 같은 참조(reference)를 공유한다?
자바스크립트에서 객체나 배열은 값 자체를 복사하는 게 아니라 메모리 주소를 복사해요. 그래서 얕은 복사를 하면, 복사본이 원본의 내부 객체(또는 배열)를 같은 주소에서 꺼내서 같이 쓰게 돼요.

이처럼, 얕은 복사는 객체의 1단계 속성만 복사하고 내부 객체는 참조를 공유하기 때문에 복사본을 수정하면 원본도 영향을 받을 수 있어요.
여기서 얕은 복사는 내부 객체(orderDetail)를 그대로 공유하기 때문에 복사본의 내부 객체를 변경하면 원본도 바뀌게 됩니다.
...)객체나 배열을 복사할 때 가장 많이 쓰이는 문법이에요.

Object.assign()빈 객체를 기준으로 기존 객체를 덮어쓰는 방식이에요.

slice(), concat())배열 자체는 참조 타입이지만 slice()와 concat()은 배열 껍데기를 새로 만들어 얕은 복사를 수행해요.
깊은 복사는 객체나 배열의 모든 중첩된 데이터까지 재귀적으로 복사하여, 복사본과 원본이 완전히 독립적인 상태를 만드는 복사 방식이에요. 깊은 복사를 수행하면 복사본의 데이터를 수정해도 원본은 절대 영향을 받지 않아요.
얕은 복사는 '주소를 공유하는 얕은 복사'지만, 깊은 복사는 '모든 메모리를 새로 복사'한다는 것이 핵심이에요.

위 예제를 보면 중첩된 객체도 모두 복사돼서 복사본을 바꿔도 원본에는 영향이 없습니다.
JSON.parse(JSON.stringify())가장 간단하게 깊은 복사를 구현할 수 있는 방법이에요. .
🚨 하지만 이 방법은
JSON.stringify를 사용하는 과정에서 한계가 있어요
undefined/function/Symbol→ JSON 포맷에서 지원하지 않아 복사 시 누락됨Date→ 문자열로 변환됨 (Date 객체가 아님)RegExp→ 빈 객체로 변환됨
🚨 JSON 방식은 순수 데이터 복사에는 적합하지만 복잡한 데이터 타입이 포함된 객체를 복사할 때는 데이터 손실 위험이 있어 안전하지 않아요.

Lodash의 _.cloneDeep()Lodash는 자바스크립트에서 널리 사용하는 유틸리티 라이브러리로, _.cloneDeep()은 함수, Date, RegExp 등 복잡한 데이터 타입까지 복사할 수 있어요.

React에서는 상태 관리에서 불변성 유지가 매우 중요해요. React는 상태가 변경될 때 얕은 비교(shallow equality)를 통해 리렌더링 여부를 판단하기 때문이에요.

React는 product 객체의 1단계만 비교하기 때문에 details 객체의 참조가 동일하다고 인식해요. 복사본의 내부 데이터가 변경되어도 React는 상태가 바뀌지 않았다고 판단할 수 있어요.
🚨 이로 인해 화면이 리렌더링되지 않거나 상태 업데이트가 제대로 반영되지 않는 문제가 발생할 수 있어요. 상태를 안전하게 관리하려면 최상위 객체뿐 아니라 필요한 하위 객체의 참조도 반드시 새로 생성해줘야 해요.

이 방법은 얕은 복사를 단계적으로 반복해 필요한 하위 객체를 새로 생성하는 패턴이에요. 깊은 복사는 아니지만, React에서 권장하는 안전한 상태 관리 방법이에요.
React는 최상위 객체의 참조가 바뀌어야 상태가 변경됐다고 인식하기 때문에, 하위 객체까지 새로 생성해주면 리렌더링이 정상적으로 발생해요.
이 방식은 복사해야 하는 깊이가 얕거나 구조가 단순할 때 적합해요. 만약 복사해야 하는 깊이가 많거나 구조가 복잡한 경우, Lodash의 _.cloneDeep()을 사용하는 것이 더 안전해요.
React 환경에서는 불변성 유지가 핵심이기 때문에 복사 방법에 대한 정확한 이해가 리렌더링과 상태 관리의 안정성으로 바로 이어질 수 있어요. 💪🏻
이 글은 공식 문서를 기반으로 내용을 정리한 포스팅입니다.
혹시 내용 중 틀린 부분이나 보완할 부분이 있다면 댓글로 남겨주시면 감사하겠습니다. 🙏🏻