블로그로 돌아가기
Javascript

javascript shallow copy: 개념부터 실무 적용까지 한 번에

javascript shallow copy 핵심 개념과 실무 예제를 통해 얕은 복사의 동작, 주의점, 대안(깊은 복사)까지 정리합니다. 코드 예제와 체크리스트로 바로 적용하세요. 실무 적용 흐름과 자주 하는 실수까지 함께 확인할 수 있습니다.

shallow-copyobject-cloningarray-copyjavascript-best-practicesimmutable-patterns
자바스크립트 얕은 복사(javascript shallow copy) 동작을 설명하는 다이어그램 이미지

javascript shallow copy이 필요한 상황

javascript shallow copy는 객체나 배열의 최상위 속성만 복사하고 중첩된 객체는 참조를 유지합니다. 이 글에서는 javascript shallow copy가 무엇인지, 언제 사용해야 하는지, 실무에서 자주 쓰는 기법(스프레드 연산자, Object.assign, Array.slice 등)의 예제와 한계, 대안까지 설명합니다. 목표는 개념 이해, 바로 적용 가능한 코드, 그리고 운영·보안·성능 관점의 주의점을 명확히 전달하는 것입니다.

javascript shallow copy 핵심 개념

얕은 복사(shallow copy)는 원본 객체의 첫 단계 프로퍼티들만 새로운 객체로 복제합니다. 따라서 복제된 객체의 중첩된 참조(객체, 배열)는 원본과 동일한 메모리 주소를 가리킵니다. 값형(primitive)은 복사되어 독립적이지만 참조형(reference)은 공유됩니다.

핵심 포인트:

  • 원시값(number, string, boolean, null, undefined, symbol)은 값 복사
  • 객체, 배열, 함수 등은 참조 복사
  • 얕은 복사는 빠르고 간단하지만 중첩 구조 변경 시 사이드 이펙트 발생 가능

javascript shallow copy 실무 예제

아래 예제는 스프레드 연산자, Object.assign, Array.prototype.slice를 사용한 얕은 복사 예제입니다.

// 객체: 스프레드 연산자 const original = { a: 1, b: { c: 2 } }; const shallow = { ...original }; shallow.a = 10; shallow.b.c = 99; // 원본도 변경됨(중첩 참조 공유) console.log(original.a); // 1 console.log(original.b.c); // 99 // 객체: Object.assign const assigned = Object.assign({}, original); // 배열: slice, spread const arr = [1, { x: 1 }]; const arrShallow = arr.slice(); arrShallow[1].x = 42; // 원본 배열의 객체가 변경됨 // 실무 패턴: 불변성 유지(최상위만 복사 후 필요한 경로만 새 객체로 교체) function updateNested(state, newValue) { return { ...state, nested: { ...state.nested, value: newValue } }; }

언제 쓰면 좋고 언제 피해야 할까

언제 쓰면 좋은가:

  • 최상위 프로퍼티만 변경할 때(예: 상태 관리에서 루트 레벨 업데이트)
  • 성능과 간결성이 중요하고 중첩 객체를 수정하지 않을 때
  • 불변성 유지의 일부로 얕은 복사 후 경로별로 깊이 복사할 계획일 때

언제 피해야 하는가:

  • 깊게 중첩된 구조를 한 번에 복제해야 할 때
  • 복제 대상에 Date, RegExp, Map, Set, 함수, 순환 참조가 포함될 때
  • 보안상 민감 데이터가 중첩 객체로 포함되어 원본과 공유되면 안 될 때

비교표: 얕은 복사 기법 비교

구분항목1항목2항목3
내용방법장점단점
내용스프레드(...)간결, 현대적 문법중첩 객체는 참조 공유
내용Object.assign({}, obj)레거시 지원, 명시적중첩 객체는 참조 공유
내용Array.slice()배열 복사 간단요소가 객체면 참조 공유
내용lodash.cloneDeep()깊은 복사 가능성능 비용 큼

판단 기준: 언제 얕은 복사로 충분한가

  • 구조 깊이: 중첩 레벨이 낮고 변경 범위가 루트 수준이면 얕은 복사로 충분
  • 변경 패턴: 변경하는 키가 단일 경로라면 부분적으로 깊은 복사를 적용
  • 데이터 형식: Date/Map/Set 등 특수 객체가 없으면 얕은 복사 사용 가능

실무 체크리스트

  • 변경할 프로퍼티가 최상위인지 확인했는가?
  • 중첩 객체를 변경할 필요가 있는지 검토했는가?
  • 참조 공유로 인한 사이드 이펙트를 예상하고 있는가?
  • 필요시 lodash.cloneDeep 또는 구조적 복사를 고려했는가?
  • 성능 임팩트를 측정(대량 데이터 복사 시)했는가?

성능·보안·운영 주의점

성능:

  • 얕은 복사는 깊은 복사보다 빠르지만 대량 배열이나 빈번한 복제는 비용이 누적됨
  • 반복적으로 전체 객체를 복제하는 패턴은 메모리와 CPU 사용량을 증가시킬 수 있음

보안:

  • 민감한 정보가 중첩 객체로 포함되어 있는 경우 얕은 복사 후 원본 참조를 통해 노출될 수 있음
  • 복구 불가능한 민감 데이터는 별도의 깊은 복사 또는 클렌징 절차 필요

운영:

  • 로그나 직렬화(예: JSON.stringify) 전 객체가 참조로 공유되는지 확인
  • 순환 참조가 있으면 JSON.stringify 실패 가능, 적절한 처리 필요

대안 및 권장 패턴

  • 깊은 복사가 필요하면 structuredClone(브라우저/Node 최신), lodash.cloneDeep 사용 고려
  • 상태 관리에서는 위에서 아래로 불변성 패턴(부분 복사)을 권장
  • 특수 객체(Date, Map 등)는 별도 복사 로직 작성

자주 묻는 질문

Q1: javascript shallow copy와 deep copy 차이는 뭔가요? A1: 얕은 복사는 최상위 프로퍼티만 복사하고 중첩 객체는 참조를 공유합니다. 깊은 복사는 중첩된 모든 객체를 재귀적으로 복사해 원본과 완전히 분리합니다.

Q2: Object.assign과 스프레드 중 어느 것을 쓰는 게 좋나요? A2: 기능적으로 유사합니다. 스프레드는 문법이 간결해 자주 사용되며, 레거시 환경에서는 Object.assign이 더 호환성이 좋습니다. 둘 다 얕은 복사입니다.

Q3: JSON.parse(JSON.stringify(obj))로 깊은 복사해도 되나요? A3: 간단한 데이터 구조에서는 동작하지만 Date, Map, Set, 함수, undefined, 순환 참조를 처리하지 못하므로 주의해야 합니다.

Q4: React 상태관리에서 얕은 복기로 충분한가요? A4: 대부분의 경우 루트 수준 또는 변경 경로에 대해 부분 복사(얕은 복사 + 필요한 경로만 새 객체 생성) 방식으로 충분합니다. 중첩 객체를 직접 변경하면 리렌더링 이슈나 사이드 이펙트가 발생할 수 있습니다.

실무 적용 예제: 실제 컴포넌트 상태 업데이트

// 상태: { user: { name, meta: { lastLogin } }, items: [] } function updateLastLogin(state, newTime) { return { ...state, user: { ...state.user, meta: { ...state.user.meta, lastLogin: newTime } } }; }

이 패턴은 얕은 복사를 기반으로 변경 경로만 깊게 복사해 불변성을 유지합니다.

추가 팁

  • structuredClone API는 브라우저/Node에서 깊은 복사를 안전하게 지원할 경우 우선 고려
  • Lodash의 cloneDeep은 강력하지만 성능 비용을 항상 테스트
  • 라이브러리(Immutable.js 등)를 도입하면 대규모 상태 관리에서 안전함 제공

정리

  • javascript shallow copy는 최상위 속성만 복사하고 중첩 객체는 참조를 공유함
  • 스프레드, Object.assign, Array.slice는 얕은 복사를 수행
  • 중첩 변경이 필요하면 부분적으로 경로 복사를 적용하거나 깊은 복사 사용
  • 성능과 보안 관점에서 복사 전략을 사전 검토하고 체크리스트를 따를 것
  • structuredClone, lodash.cloneDeep 등 대안의 장단점을 상황에 맞게 선택