Skip to main content

Command Palette

Search for a command to run...

깊은복사와 얕은 복사

자바스크립트 깊은 복사와 얕은 복사

Updated
5 min readView as Markdown
깊은복사와 얕은 복사
L

직면하는 모든 문제에 유치한 것은 없으며, 의미 없는 삽질 또한 없다고 믿습니다.


🌷 얕은 복사와 깊은 복사

최근들어 내가 기본이 아직 많이 부족하다는 것을 정말 많이 느낀다. 짧은 시간 공부하고 취업해서 개발자라고 이야기를 하고 있으니.. 그럴 수 밖에. 아직 어디를 가면 스스로를 개발자라고 소개하기가 부끄러울 때가 많다. 그래서 기본이 부족했던 부분들을 다시 열심히 공부해서 차곡차곡 쌓아야겠다는 생각이들었다.

오늘은 얕은 복사와 깊은 복사에 대해 조금 더 깊게 확인하고 제대로 이해를 해봐야겠다고 생각하고 정리를 해봤다. 지금 회사 입사를 할 때 받았던 질문이었는데 그 때는 정말 두루뭉술하게만 대답하고 이해했던 것 같아서, 이참에 제대로 알고 가보자!

🖐🏻 얕은 복사와 깊은 복사를 알아보기 전에,


📝 데이터 타입

  • 원시형

  • 참조형

👉🏻 자세히 알아보기

📝 데이터 타입에 따른 메모리 동작

  • 원시형
// 변수 poke 에 문자열 "피카츄"를 할당
var poke = "피카츄";

/* 메모리 동작

1. &17 의 값에 undefined 할당
2. 식별자 poke 에 &17 할당
3. 새로운 공간인 &27 에 "피카츄"을 저장
4. poke 에 &27할당
*/

* & : 메모리 주소를 의미 ex. &27 는 메모리의 27번 주소

  • 참조형 - property 하나를 가질 경우
// 변수 startingPokemon 에 객체를 할당
var starting = {
    name : "이상해씨"
};

/* 메모리 동작

1. &18의 값에 undefined 를 저장
2. 식별자 starting 에 &28 을 할당 "주소의 이름이 됨!"
3. 새로운 공간인 &28 에 객체를 저장할 공간의 주소인 &37 를 할당
  - 즉, &28 번 주소의 값이 &37 이 됨
  - 원시형 데이터는 &27번 주소의 값이 "피카츄"
4. &12(name) 에 "이상해씨" 라는 문자열 값을 저장
5. starting 에 &28 을 할당

* 결과
  - starting 변수의 값은 &18
  - &18 에는 객체 프로퍼티를 저장하고 있는 저장 공간의 주솟값(&37)를 가짐
  - 객체는 주솟값을 이용해 참조
*/

  • 참조형 - 2가지 이상의 프로퍼티를 가질 경우
// 변수 startingPokemon 에 객체를 할당
var myPoke = {
    first : "피카츄"
   second : "이상해씨"
};

🙋🏻‍♀️ 복사의 종류

deep copy : A deep copy means that all of the values of the new variable are copied and disconnected from the original variable.

shallow copy : A shallow copy means that certain (sub-)values are still connected to the original variable.

얕은 복사

  • 참조 복사 (reference copy)

  • 객체 복사 (object copy)

  • ex ) Array.prototype.slice(), Object.assign(), Spread 연산자 (전개 연산자)

깊은 복사

  • 깊은 복사 (deep copy)

  • ex) JSON.parse() , JSON.stringify , 재귀 함수를 구현한 복사, Lodash 라이브러리 사용

/* 1. 중첩된 객체 예시 */
var startingPokemon = {
        generation: '1세대',
        pokemon : {
            name : "이상해씨",
            number : 1
        }        
    };

# 참조 복사
var referenceCopy = startingPokemon;

# 객체 복사
var shallowCopy = shallowCopy(startingPokemon);

const shallowCopy = ( targetObj ) => {
    var returnObj = {};

    // targetObj 를 순회하며 
  // key-targetObj[key] (key-value) 쌍으로 returnObj 추가
  for (var key in targetObj) {
        returnObj[key] = targetObj[key];
    }

    return returnObj;  // 새로 만들어진 객체를 반환
};

# 깊은 복사
var deepCopy = deepCopy(startingPokemon);

const deepCopy = ( targetObj ) => {
    var returnValue = {};

    if (typeof targetObj === 'object' && targetObj !== null) {

    // targetObj 가 객체라면 프로퍼티를 재귀 형식으로 호출
        for (var key in targetObj) {
            returnValue[key] = deepCopy(targetObj[key]);
        }
    } else {
    // target 이 객체가 아니라면 값 반환
        returnValue = target;
    }
    return returnObj;
}

💁🏻‍♀️ 복사 결과

1️⃣ 얕은 복사 - 참조 복사 (주솟값 복사)

# 참조 복사 // referenceCopy

var starting = {
        generation: '1세대',
        pokemon : {
            name : "이상해씨",
            number : 1
        }        
    };

var rfc = starting;

복사 결과 확인

# 원본객체

&11 (shallowCopy)
    - &21
        - &31 (generation)
        - &32 (pokemon)
            - &24 (name) : "이상해씨"
            - &25 (number) : 1
# 참조 복사

&12 (shallowCopy)
    - &21
        - &31 (generation)
        - &32 (pokemon)
            - &24 (name) : "이상해씨"
            - &25 (number) : 1

2️⃣ 얕은 복사 - 객체 복사

// 객체 복사 - shallowCopy

var starting = {
        generation: '1세대',
        pokemon : {
            name : "이상해씨",
            number : 1
        }        
    };

var sc = shallowCopy(starting);

const shallowCopy = ( targetObj ) => {
    var returnObj = {};

    // targetObj 를 순회하며 
    // key-targetObj[key] (key-value) 쌍으로 returnObj 추가
  for (var key in targetObj) {
        returnObj[key] = targetObj[key];
        /*
            returnObj['generation'] = targetObj['generation'];
            returnObj['pokemon'] = targetObj['pokemon']; // 객체를 가져옴
        */
    }

    return returnObj;  // 새로 만들어진 객체를 반환
};
  • 복사 결과 확인
# 원본객체

&11 (shallowCopy)
    - &21
        - &31 (generation)
        - &32 (pokemon)
            - &24 (name) : "이상해씨"
            - &25 (number) : 1
# 객체 복사

&13 (shallowCopy)
    - &26
        - &51 (generation)
        - &32 (pokemon)
            - &24 (name) : "이상해씨"
            - &25 (number) : 1

3️⃣ 깊은 복사

# 깊은 복사

var starting = {
        generation: '1세대',
        pokemon : {
            name : "이상해씨",
            number : 1
        }        
    };

var deepCopy = deepCopy(starting);

const deepCopy = ( targetObj ) => {
    var returnValue = {};

    if (typeof targetObj === 'object' && targetObj !== null) {

    // targetObj 가 객체라면 프로퍼티를 재귀 형식으로 호출
        for (var key in targetObj) {
            returnValue[key] = deepCopy(targetObj[key]);
        }
    } else {
    // target 이 객체가 아니라면 값 반환
        returnValue = target;
    }
    return returnObj;
}
  • 복사 결과

    • 모든 주소의 값이 달라짐
# 원본객체

&11 (shallowCopy)
    - &21
        - &31 (generation)
        - &32 (pokemon)
            - &24 (name) : "이상해씨"
            - &25 (number) : 1
# 깊은 복사

&14 (dc)
    - &27
        - &61 (generation)
        - &62 (pokemon)
            - &71 (name) : "이상해씨"
            - &72 (number) : 1

👩🏻‍💻 각 객체들의 주솟값 비교 모아보기

  • 🔗 : 원본 객체와 연결

  • 🆕 : 원본 객체가 아닌 새로운 값

# 원본객체

&11 (shallowCopy)
    - &21
        - &31 (generation)
        - &32 (pokemon)
            - &24 (name) : "이상해씨"
            - &25 (number) : 1
# 참조 복사

&12 (shallowCopy)
    - &21 🔗
        - &31 (generation)
        - &32 (pokemon)
            - &24 (name) : "이상해씨"
            - &25 (number) : 1
# 깊은 복사

&14 (dc)
    - &27 🆕 
        - &61 (generation) 🆕 
        - &62 (pokemon) 🆕 
            - &71 (name) : "이상해씨"
            - &72 (number) : 1
# 얕은 복사

&13 (shallowCopy)
    - &26 🆕 
        - &51 (generation) 🆕 
        - &32 (pokemon) 🔗
            - &24 (name) : "이상해씨"
            - &25 (number) : 1

🌝 결론

  • 복사한 객체가 원래 객체에 연결이 되어있다면 (주솟값을 참고) 얕은복사

  • 복사한 객체가 원래 객체에 연결되어 있지 않다면 깊은복사

  • 느낀 점

    • 직접 데이터 주소 구조를 그려가며 이해해보니 확 머리에 들어왔던… 어렵다고 이해할 수 없는 것들은 없는 것 같다. 열심히 파다보면 역시 내 것이 되는 ㅠ_ㅠ 열심히 하자..!
  • 혹시 내용 중 틀린 점이 있다면 알려주세요! 응애 개발자의 성장을 위해..!


참고