들어가며 🚪
자바스크립트로 개발을 하다 보면 객체나 배열을 복사해야 하는 상황이 자주 발생합니다. 특히 React나 Node.js 애플리케이션에서는 데이터의 불변성(immutability)을 유지하기 위해 원본 데이터를 직접 수정하지 않고 복사본을 만들어 작업하는 것이 중요합니다. 이때 '얕은 복사(Shallow Copy)'와 '깊은 복사(Deep Copy)'의 차이를 이해하는 것이 매우 중요한데요, 이 두 개념을 쉽게 이해해 봅시다! 👨💻👩💻
원시 타입 vs 참조 타입 💡
먼저, 자바스크립트의 데이터 타입을 이해해야 합니다
원시 타입(Primitive Types) 📌
- String, Number, Boolean, null, undefined, Symbol, BigInt
- 값 자체가 변수에 저장됨
- 복사할 때 값이 그대로 새 변수에 복사됨
참조 타입(Reference Types) 🔗
- Object, Array, Function
- 메모리 주소(참조)가 변수에 저장됨
- 복사할 때 주소값만 복사되어 같은 데이터를 가리키게 됨
바로 여기서 얕은 복사와 깊은 복사의 차이가 발생합니다!
얕은 복사(Shallow Copy) 란? 🌊
얕은 복사는 객체의 최상위 속성들만 새로운 메모리에 복사하고, 중첩된 객체는 여전히 원본 객체의 참조를 유지하는 복사 방법입니다.
얕은 복사의 특징 🏷️
- 최상위 속성만 새로운 메모리에 복사됨
- 중첩된 객체나 배열은 참조만 복사됨
- 중첩된 객체를 수정하면 원본도 영향을 받음
얕은 복사 방법들 🛠️
1. Object.assign() 사용하기
const original = { name: "철수", scores: [90, 85, 95] };
const copied = Object.assign({}, original);
copied.name = "영희"; // OK: 최상위 속성 변경은 원본에 영향 없음
copied.scores.push(100); // 주의: 중첩 객체 변경은 원본도 변경됨!
console.log(original.scores); // [90, 85, 95, 100] 😱
2. 스프레드 연산자(...) 사용하기
const original = { user: { name: "철수", age: 25 }, active: true };
const copied = { ...original };
copied.active = false; // OK: 최상위 속성만 독립적으로 변경
copied.user.age = 26; // 주의: user 객체는 여전히 참조로 연결됨!
console.log(original.user.age); // 26 😱
3. 배열의 얕은 복사
const originalArray = [1, 2, { id: 1, value: "a" }];
const copiedArray = [...originalArray]; // Array.slice()도 동일하게 작동
copiedArray[0] = 99; // OK: 원시값 변경은 독립적
copiedArray[2].value = "b"; // 주의: 객체 참조는 공유됨!
console.log(originalArray[2].value); // "b" 😱
깊은 복사(Deep Copy) 란? 🏊♂️
깊은 복사는 객체의 모든 수준의 속성을 새로운 메모리에 완전히 복사하는 방법입니다. 중첩된 객체까지 모두 새로운 참조를 갖게 됩니다.
깊은 복사의 특징 📝
- 모든 중첩 수준의 객체가 새롭게 생성됨
- 원본 객체와 완전히 독립적인 사본이 만들어짐
- 어느 한쪽을 수정해도 다른 쪽에 영향을 주지 않음
깊은 복사 방법들 🔧
1. JSON.parse() + JSON.stringify() 사용하기
const original = { name: "철수", scores: [90, 85, 95], info: { age: 20 } };
const deepCopied = JSON.parse(JSON.stringify(original));
deepCopied.scores.push(100); // 안전: 중첩 배열도 독립적
deepCopied.info.age = 21; // 안전: 중첩 객체도 독립적
console.log(original.scores); // [90, 85, 95] ✅
console.log(original.info.age); // 20 ✅
2. lodash의 cloneDeep 사용하기 (라이브러리 사용)
import _ from 'lodash';
const original = {
user: { name: "철수", hobbies: ["축구", "게임"] },
created: new Date()
};
const deepCopied = _.cloneDeep(original);
// 모든 수준의 변경이 안전함!
deepCopied.user.hobbies.push("코딩");
console.log(original.user.hobbies); // ["축구", "게임"] ✅
3. React에서 중첩 객체 복사하기 (함수형 접근)
// React에서 상태 업데이트 시 중첩 객체 안전하게 수정하기
const updateUserHobby = (user) => {
return {
...user,
hobbies: [...user.hobbies, "코딩"],
info: { ...user.info, age: user.info.age + 1 }
};
};
JSON 방식의 깊은 복사 주의점 ⚠️
JSON을 이용한 깊은 복사는 간단하지만 몇 가지 제한이 있습니다
- 함수, Map, Set, Date 객체 등은 제대로 복사되지 않음
- undefined와 같은 특수값이 손실될 수 있음
- 순환 참조(Circular Reference)가 있으면 오류 발생
실전 사용 예시: React에서 중첩된 상태 업데이트 🔄
React에서는 상태의 불변성을 유지하는 것이 중요합니다. 다음은 중첩된 객체를 안전하게 업데이트하는 예시입니다:
function ProfileEditor() {
const [user, setUser] = useState({
name: "김철수",
contact: { email: "chulsoo@example.com", phone: "010-1234-5678" },
tags: ["개발자", "리액트"]
});
// 중첩된 속성 업데이트 (깊은 복사 원리 활용)
const updateEmail = (newEmail) => {
setUser({
...user, // 1단계 얕은 복사
contact: { // contact 객체 새로 생성
...user.contact, // 기존 contact 속성 복사
email: newEmail // email만 업데이트
}
});
};
}
얕은 복사 vs 깊은 복사: 언제 무엇을 사용할까? 🤔
얕은 복사가 적합한 경우
- 단순한 구조의 객체를 다룰 때
- 성능이 중요하고 중첩 객체를 수정할 일이 없을 때
- 의도적으로 참조를 공유하고 싶을 때
깊은 복사가 적합한 경우
- 복잡한 중첩 구조의 객체를 안전하게 복사할 때
- 원본 데이터의 불변성을 완벽하게 유지해야 할 때
- React, Redux 등에서 상태 관리 시
정리 📝
- 얕은 복사: 최상위 속성만 새로 복사, 중첩 객체는 참조 공유 🌊
- 깊은 복사: 모든 중첩 수준까지 완전히 새로 복사 🏊♂️
- 주의점: 복사 방법에 따라 사이드 이펙트가 발생할 수 있음 ⚠️
자바스크립트에서 객체와 배열을 다룰 때, 얕은 복사와 깊은 복사의 차이를 이해하는 것은 버그를 예방하고 코드의 예측 가능성을 높이는 데 매우 중요합니다. 특히 React나 Redux와 같은 프레임워크에서는 상태의 불변성을 유지하기 위해 이 개념을 제대로 이해하고 있어야 합니다! 💪
'1일 1CS(Computer Science)' 카테고리의 다른 글
리액트의 Strict Mode에 대해서 설명해주세요. (0) | 2025.04.07 |
---|---|
인터넷 창에 www.google.com를 입력하면 무슨 일이 일어나는지 설명해주세요. (0) | 2025.04.07 |
트랜잭션 격리수준은 무엇인가요? (0) | 2025.04.07 |
브라우저 렌더링 파이프라인에 대해서 설명해주세요. (0) | 2025.04.04 |
리액트에서 성능 최적화를 위해 적용할 수 있는 방법들을 설명해주세요. (0) | 2025.04.04 |