til

자바스크립트 집중 학습 3일 차 @데이터 타입(심화), 얕은/깊은 복사

fpzmfks 2024. 7. 25. 23:33

오늘은 어제 하던 개인과제를 일단락하고 다시금 자바스크립트 집중 학습을 시작했다. 꼭 알아야해서 배우고 있는 것이겠지만 이제 슬슬 학습이 버거워지는 시점이다. 

 

데이터 타입(심화)

먼저 데이터 타입(심화)를 정리해보자. 데이터 타입(심화)는 데이터의 타입이 기본형/참조형으로 갈린다는 것을 명시했다. 이러한 데이터 타입은 저장 방식과 불변성 여부로 갈리는데, 표로 정리하면 아래와 같다. 

 

데이터 타입 기본형 참조형
예시 숫자, 문자, 불리언 등 배열, 객체 등
저장 방식 값이 담긴 
=> 주소값을 저장
값이 담긴
=> 주소값을 저장한 묶음
=> 가리키는 주소값을 저장
불변성 여부 불변성 O 불변성 X

 

이러한 구조를 다시금 표로 정리하자면 이렇게 된다. 

데이터 저장 공간 저장 공간 1 저장 공간 2(불변성O) 저장 공간 3(불변성X)
(변조 가능)
기본형 [변수명, 저장 공간 2의 주소] 데이터 X
참조형 [변수명, 저장 공간 3의 주소] 데이터1, 데이터2, 데이터3, 데이터4 [0, 데이터 1의 주소],[1, 데이터 2의 주소], [2, 데이터 3의 주소], [3, 데이터 4의 주소]

 

위 표를 보면 기본형은 불변성을 가진 저장 공간에만 데이터를 저장했기 때문에 불변성을 가지고 있지만, 참조형은 불변성을 가지지 않은 저장 공간에도 데이터를 저장했기 때문에 불변성을 가지지 않았다는 것을 알 수 있다. 

 

 

이러한 데이터 저장 구조는 자유로운 데이터 변환메모리의 효율적 관리를 위한 것인데,

 

먼저 자유로운 데이터 변환을 살피자면, 만약 저장 공간을 하나만 가지고 있을 경우,

 

데이터를 변환할 때마다

=> 해당 데이터가 가지는 메모리 값이 바뀌면서

=> 다른 데이터들과 저장 공간을 두고 충돌하는 경우가 있을 수 있다.

 

하지만 이와 같이 저장 공간을 여러 개 두고 주소를 불러오면,

 

데이터를 변환하지 않고 새로운 데이터를 생성함

=>새로운 데이터의 주소를 가져옴  으로써 데이터들 간의 충돌을 줄일 수 있다.

 

다음으로 메모리의 효율적 관리 측면에서 보자면, 이렇게 데이터를 직접 저장하지 않고 주소로 데이터를 저장하는 경우 같은 값을 여러 번 사용해야 할 때 이득을 볼 수 있다. 

 

실질적으로 데이터를 저장하는 저장공간2는 변조가 불가능하므로 부담없이 몇 번이고 값을 가져다 쓸 수 있는데,

하나의 데이터를 저장하는 데에 필요한 메모리가 저장공간1=2byte, 저장공간2=8byte일 때, 

같은 데이터 값을 1000번 저장하는 데에 필요한 메모리는 (2byte*1000)+8byte로

저장공간2의 메모리 부담량이 없어져서 실질적으로 필요한 메모리 양이 줄어든다. 

 

표를 보면 이렇다.

 

  하나 저장 1000개 저장 차(아낄 수 있는 메모리 양)
저장공간이 1개 10btye 10000byte 7992byte
저장공간이 2개 2+8byte (2000+8)byte

 

 

또 이러한 데이터들은 byte를 단위로 가진 메모리로 구성되어 있고, 이러한 각 데이터 형태마다 필요한 메모리 규격의 크기는 차이가 있다(문자:1,2byte, 숫자:8byte). 자바스크립트에서는 데이터 주소의 형태로 값을 저장하면서 각 데이터의 메모리 규격 차이로 인해 발생할 수 있는 문제를 줄인 것이다. 


 

얕은 복사/깊은 복사

다음은 복사이다. 처음 복사라는 개념을 알게 되었을 때 복사가 복사지 왜 이렇게 나뉘어 있는지 모르겠다는 생각을 했다. 하지만 데이터에 대해 공부하고 난 뒤 이것이 코딩에서 굉장히 중요한 영역 중 하나라는 것을 알게 되었다. 특히 참조 데이터 형태에서 어떤 복사를 사용했나로 고려해야 하는 것들이 달라진다. 

 

먼저 간단한 일반 복사를 살펴보자. 일반 복사는 변수명 = 변수명1 의 형태로 데이터를 복사한다. 이 경우 변수명과 변수명1은 같은 주소값 묶음을 가지고 있으므로 어느 한 쪽에서 주소값 묶음에 접근해서 주소(데이터)를 바꾸어버리면 다른 한 쪽의 주소(데이터)도 바뀌어 버린다. 표로 정리하자면 이렇다. 

일반 복사 저장공간1 저장공간2 저장공간3 변수명 1에서

저장공간3에 접근해서

2의 데이터 주소 변조
저장공간1 저장공간2 저장공간3
변조 여부   X O   X O
변수명 변수명
3의 주소
데이터 3 주소
2의 데이터 주소
변수명
3의 주소
데이터, 데이터1 3 주소
2의 데이터1 주소
변수명1 변수명1
3의 주소
변수명1
3의 주소

 

즉 일반 복사란 변수명과 변수명1이 같은 데이터를 공유하고 있는 상태를 뜻한다. 

 

얕은/깊은 복사는 이러한 문제가 일어나는 것을 막을 수 있는 복사 방식으로, 저장공간3에 새로운 데이터 주소를 생성하도록 한다. 

얕은/깊은
복사
저장공간1 저장공간2 저장공간3 변수명 1에서

저장공간3에 접근해서

2의 데이터 주소 변조
저장공간1 저장공간2 저장공간3
변조 여부   X O   X O
변수명 변수명
3의 주소
데이터 3 주소
2의 데이터 주소
변수명
3의 주소
데이터, 데이터1 3 주소
2의 데이터 주소
변수명1 변수명1
3의 주소1
3 주소1
2의 데이터 주소
변수명1
3의 주소1
3 주소1
2의 데이터1 주소

 

이렇게 이전에 일반 복사에서 변조 결과가 공유되던 것과 다르게 변수명1이 따로 가진 저장공간3의 데이터만 변조된 것을 확인할 수 있다. 

 

다만 이렇게 얕은/깊은 복사를 하려면 조금 번거로운 과정을 거쳐야한다. 

 

먼저 얕은 복사를 살펴보자. 아래는 객체를 복사하는 함수이다. 

var copyObject = function (target) {
    var result = {};
 
    for (var prop in target) {
        result[prop] = target[prop];
    }
    return result;
}

이렇게 빈 객체 선언 => for문을 통해 새 객체 생성 => 새 객체 반환

의 과정을 거치는 함수를 사용하면 쉽게 얕은 복사를 할 수 있다. 

 

다만 이러한 얕은 복사는 복사하려는 참조 데이터가 다시 내부에 참조 데이터를 가지고 있는 것을 복사할 수 없다.

var user = {
    name: 'wonjang',
    urls: {
        portfolio: 'http://github.com/abc',
        blog: 'http://blog.com',
        facebook: 'http://facebook.com/abc',
    }
};

때문에 이러한 복잡한 형태의 데이터를 복사하기 위해서는 깊은 복사를 해야한다. 

 

아래가 재귀적 수행을 통해 깊은 복사를 한 것이다. 

var copyObjectDeep = function(target) {
    var result = {};
    if (typeof target === 'object' && target !== null) {
        for (var prop in target) {
            result[prop] = copyObjectDeep(target[prop]);
        }
    } else {
        result = target;
    }
    return result;
}

먼저 빈 객체 result를 선언하고

for문을 이용하여 새 객체를 생성하는 동시에

이전에 얕은 복사에서는 없었던 재귀적 수행

result[prop] = copyObjectDeep(target[prop]);

과 함께 if else문을 사용하여 재귀적 수행이 언제 끝날지 결정하고 돌려서

객체를 모두 복사한 뒤

새 객체가 된 result를 반환했다.

 

 

즉 

if (typeof target === 'object' && target !== null) {
        for (var prop in target) {
            result[prop] = copyObjectDeep(target[prop]);
        }
    } else {
        result = target;
    }

는 위의 이미지의 구조를 갖게 된다.