핵심 요약
JavaScript는 객체·배열을 참조로 다룬다. {...obj}나 Object.assign은 얕은 복사라 1단계 속성만 복제하고, 중첩된 객체는 여전히 같은 참조를 공유한다. 그래서 복사본의 a.b.c를 바꾸면 원본도 바뀐다. 완전히 분리하려면 깊은 복사(structuredClone 등)가 필요하다.
1. 왜 원본이 같이 바뀌나
const a = { user: { name: '철수' } }
const b = { ...a } // 얕은 복사
b.user.name = '영희'
console.log(a.user.name) // '영희' 😱 원본도 바뀜
{...a}는 user가 가리키는 참조만 복사했다. a.user와 b.user는 같은 객체다.
2. 얕은 복사 방법들
{ ...obj },Object.assign({}, obj)- 배열은
[...arr],arr.slice(),Array.from(arr) - 공통점: 1단계만 복사. 중첩은 공유
3. 깊은 복사 방법
| 방법 | 특징 |
|---|---|
structuredClone(obj) | 표준·권장. Date·Map·Set도 복제, 순환참조 OK |
JSON.parse(JSON.stringify(obj)) | 간단하지만 함수·undefined·Date 손실 |
lodash cloneDeep | 엣지케이스까지 견고, 의존성 추가 |
4. React에서 특히 중요한 이유
// ❌ 원본 배열을 직접 변경 → 리렌더 안 됨
state.items.push(x); setState(state)
// ✅ 새 참조를 만들어 불변 업데이트
setState({ ...state, items: [...state.items, x] })
React는 참조 비교로 변경을 감지한다. 원본을 mutate하면 참조가 그대로라 렌더가 안 일어난다.
5. 함정
JSON.stringify복사는undefined·함수·Symbol을 날리고Date를 문자열로 만든다- 중첩이 깊으면 필요한 부분만 새로 만드는 게 성능상 낫다(전체 깊은 복사는 비쌈)
자주 묻는 질문
structuredClone과 JSON 방식 중 뭘 써야 하나요?
브라우저·Node 최신 환경이면 structuredClone이 표준이고 Date·Map·Set·순환참조까지 처리해 더 안전합니다. 순수 데이터(문자·숫자·배열·평범한 객체)만 있고 구형 환경을 지원해야 하면 JSON 방식도 무방합니다.
깊은 복사는 항상 하는 게 안전하지 않나요?
안전하지만 비쌉니다. 큰 객체를 매번 통째로 복제하면 성능·메모리 부담이 큽니다. 실무에선 변경되는 경로만 새로 만드는 불변 업데이트(스프레드 중첩)로 필요한 부분만 복사합니다.
const로 선언하면 객체가 안 바뀌지 않나요?
const는 변수 재할당만 막습니다. 객체 내부 속성은 그대로 바꿀 수 있습니다. 내용까지 얼리려면 Object.freeze가 필요하지만 이것도 얕게만 얼립니다.

댓글 0