기본형은 불변성, 참조형은 가변성이라는 속성을 갖는다고 말합니다. 이를 잘 이해하려면 메모리와 데이터에 대한 지식이 필요하고, 나아가 '식별자'와 '변수'의 개념을 구분할 수 있어야 합니다. 이러한 과정을 통해 메모리 영역에서 자바스크립트의 데이터가 처리되는 과정을 살펴보겠습니다.
컴퓨터는 모든 데이터를 0과 1로 바꿔 기억합니다. 0 또는 1만 표현할 수 있는 하나의 메모리 조각을 비트라고 합니다. 메모리는 매우 많은 비트들로 구성되어 있는데, 각 비트는 고유한 식별자(unique identifier)를 통해 위치를 확인할 수 있습니다. 그런데 고작 0이나 1만 표현할 수 있는 비트 단위로 위치를 확인하는 것은 매우 비효율적입니다. 그보다는 몇 개씩 묶어 하나의 단위로 여긴다면 표현할 수 있는 값도 늘어나면서 동시에 검색 시간을 줄일 수도 있을 겁니다.
C/C++, 자바 등의 정적 타입 언어는 메모리의 낭비를 최소화하기 위해 데이터 타입별로 할당할 메모리 영역을 2바이트, 4바이트 등으로 나누어 정해놓았습니다. 한편 자바스크립트는 메모리 용량이 과거보다 월등하게 커진 상황에서 등장했기에 상대적으로 메모리 관리에서 자유로워졌습니다. 그래서 메모리 공간을 좀 더 넉넉하게 할당했습니다. 숫자의 경우 정수형인지 부동소수형인지 구분하지 않고 64비트, 즉 8바이트를 확보합니다.
위에서 비트는 고유한 식별자를 지닌다고 했습니다. 바이트 역시 시작하는 비트의 식별자로 위치를 파악할 수 있을 것입니다. 모든 데이터는 바이트 단위의 식별자, 더 정확하게는 메모리 주솟값(Memory Address)을 통해 서로 구분하고 연결할 수 있습니다.
보통 변수(Variable)와 식별자(Identifier)를 혼용하는 경우가 많습니다. 이에 대한 구분을 해보도록 합시다.
변수는 '변할 수 있는 수'입니다. 물론, 변수는 값이 반드시 '숫자'여야하는 것은 아닙니다. 영어 단어 Variable은 원래 '변할 수 있다라는 형용사이지만 컴퓨터 용어로 쓸 때는 '변할 수 있는 무언가'라는 명사로 확장시켰습니다. 여기서 '무언가'란 데이터를 말합니다. 숫자도 데이터이고, 문자열, 객체, 배열 모두 데이터입니다. 식별자는 어떤 데이터를 식별하는 데 사용하는 이름, 즉 변수명입니다.
변수란? 변할 수 있는 데이터
식별자란? 변수명
// 변수 선언
let a;
위의 내용은 "변할 수 있는 데이터를 만든다. 이 데이터의 식별자는 a로 한다"가 됩니다. 변할 수 있는 데이터이므로 undefined
라 하더라도 나중에 바꾸면 됩니다. 이렇게 보면 결국 변수는 변경 가능한 데이터가 담길 수 있는 공간 또는 그릇이라 할 수 있습니다.
주소 | ... | 1002 | 1003 | 1004 | 1005 | ... |
---|---|---|---|---|---|---|
데이터 | 이름: a | |||||
값: |
let a; // 변수 a 선언
a = 'abc'; // 변수 a에 데이터 할당
let a = 'abc'; // 변수 선언과 할당을 한 문장으로 표현
위와 같은 두 가지 방법에서 자바스크립트 엔진은 결국 같은 동작을 수행합니다. a라는 이름을 가진 주소를 검색해서 그곳에 문자열 'abc'를 할당하면 됩니다.
하지만 실제로는 해당 위치에 문자열 'abc'를 직접 저장하지 않습니다. 데이터를 저장하기 위한 별도의 메모리 공간을 다시 확보해서 문자열 'abc'를 저장하고, 그 주소를 변수 영역에 저장하는 식으로 이뤄집니다. 이제는 데이터의 성질에 따라 '변수 영역', '데이터 영역'으로 구분해서 설명하겠습니다.
주소 | ... | 1002 | 1003 | 1004 | 1005 | ... |
---|---|---|---|---|---|---|
데이터 | 이름: a | |||||
값: @5004 |
주소 | ... | 5002 | 5003 | 5004 | 5005 | ... |
---|---|---|---|---|---|---|
데이터 | 'abc' |
변수 영역에 값을 직접 대입하지 않고 굳이 번거롭게 다른 메모리 주소를 할당하는 것일까요? 이는 데이터 변환을 자유롭게 할 수 있게 함과 동시에 메모리를 더욱 효율적으로 관리하기 위함입니다.
주소 | ... | 1002 | 1003 | 1004 | 1005 | ... |
---|---|---|---|---|---|---|
데이터 | 이름: a | |||||
값: @5005 (변경) |
주소 | ... | 5002 | 5003 | 5004 | 5005 | ... |
---|---|---|---|---|---|---|
데이터 | 'abc' | 'abcdef' |
위와 같이 문자열 'abc'에 'def'를 추가하라고 한다면, 'abcdef'라는 문자열을 새로 만들어 별도의 공간에 저장하고, 그 주소를 변수 공간에 연결합니다. 반대로 'c'를 제거하라해도 새로 만들게 됩니다. 기존 문자열에 어떤 변환을 가하든 상관 없이 무조건 새로 만들어 별도의 공간에 저장하는 것입니다.
위에서 언급했던 것처럼 기본형 데이터인 숫자, 문자열, boolean, null, undefined, Symbol은 모두 불변값입니다. 그러나 기억해야할 것은 변경 가능성의 대상은 데이터 영역 메모리입니다.
let a = 'abc';
a = a + 'def';
let b = 5;
let c = 5;
b = 7;
먼저 위의 두 줄을 보면, 위의 예시와 같습니다. 'abc'에 'def'를 추가하면 기존의 'abc'가 'abcdef'로 바뀌는 것이 아니라(불변) 새로운 문자열 'abcdef'를 만들어 그 주소를 변수 a에 저장합니다. 두 개는 완전히 별개의 데이터인 것입니다.
b라는 변수에는 숫자 5를 할당합니다. 그러면 컴퓨터는 일단 데이터 영역에서 5를 찾고, 없으면 그때 데이터 공간을 하나 만들어 저장합니다. 그 주소를 b에 저장합니다. c라는 변수도 같은 숫자인 5를 할당하려 합니다. 컴퓨터는 데이터 영역에서 5를 찾습니다. 위에서 이미 만들어놨으니 그 주소를 재활용합니다.
마지막 줄에서는 변수 b의 값을 7로 바꾸고자 합니다. 그러면, 기존의 데이터인 5를 7로 바꾸는 것이 아니라 기존에 저장했던 7을 찾아서 있으면 재활용하고, 없으면 새로 만들어 b에 저장합니다. 결국 5와 7 모두 다른 값으로 변경할 수 없습니다.
위와 같은 내용이 기본 데이터의 불변성이라는 성질입니다. 한 번 만들어진 값은 가비지 컬렉팅을 당하지 않는 한 영원히 변하지 않습니다.
기본형 데이터와 달리 참조형 데이터는 모두 가변값이라 했습니다. 기본적인 성질은 가변값인 경우가 많지만 설정에 따라 변경 불가능한 경우도 있고, 아예 불변값으로 활용하는 방안도 있습니다. 우선 참조형 데이터를 변수에 할당하는 과정부터 살펴봅시다.
let obj1 = {
a = 1,
b = 'bbb'
};
주소 | 1001 | 1002 | 1003 | 1004 | ... |
---|---|---|---|---|---|
데이터 | 이름: obj1 | ||||
값: @5001 |
주소 | 5001 | 5002 | 5003 | 5004 | ... |
---|---|---|---|---|---|
데이터 | @7103 ~ ? | 1 | 'bbb' |
주소 | 7103 | 7104 | 7105 | 7106 | ... |
---|---|---|---|---|---|
데이터 | 이름: a | 이름: b | |||
값: @5003 | 값: @5004 |
let obj1 = {
a = 1,
b = 'bbb'
};
obj1.a = 2;
위처럼 obj1의 a 프로퍼티에 숫자 2를 할당하려 합니다. 데이터 영역에서 숫자 2를 검색합니다. 검색 결과가 없으므로 빈 공간인 @5005에 저장하고, 이 주소를 @7103에 저장합니다.
주소 | 1001 | 1002 | 1003 | 1004 | 1005 | ... |
---|---|---|---|---|---|---|
데이터 | 이름: obj1 | |||||
값: @5001 |
주소 | 5001 | 5002 | 5003 | 5004 | 5005 | ... |
---|---|---|---|---|---|---|
데이터 | @7103 ~ ? | 1 | 'bbb' | 2 |
주소 | 7103 | 7104 | 7105 | 7106 | 7107 | ... |
---|---|---|---|---|---|---|
데이터 | 이름: a | 이름: b | ||||
값: @5005 | 값: @5004 |
이와 같이 프로퍼티에 재할당이 일어났지만, 변수 obj1이 바라보고 있는 주소는 @5001로 변하지 않았습니다. 즉, '새로운 객체'가 만들어진 것이 아니라 기존의 객체 내부의 값만 바뀐 것입니다.
이외에도 중첩된 참조형 데이터(객체)의 프로퍼티 할당 등에 대해 추가적으로 학습하시면 데이터 타입에 대해 더욱 자세히 이해하실 수 있습니다.
다음 시간에는 이러한 데이터의 가변성, 불변성에 따라 생기는 얕은 복사와 깊은 복사에 대해 살펴보도록 하겠습니다.🥸