본문으로 바로가기

기본형과 자료형의 차이

자바스크립트의 두가지 타입인 기본형자료형의 정의에 대해 알아보고 두 타입간의 차이점이 발생하는 원인에 대해 알아보겠습니다.

 


 

자바스크립트 데이터 타입은 크게 두가지인 원시형(Primitive Type)참조형(Reference Type)으로 분리됩니다.
기본(원시)형에는 Number, String, Boolean, null, undefined 가 있으며 ES6 에서는 Symbol 도 추가되었습니다.
참조형은 대표적으로 객체(Object)가 있고 그 하위에 배열(Array), 함수(Function), 정규표현식(RegExp) 등이 있으며, ES6에서는 Map, Set, WeakMap, WeakSet 등도 추가되었습니다.

 

두 타입의 가장 대표적인 차이로는 기본형에는 바로 값을 그대로 할당한다는 것이고 참조형에는 값이 저장된 주소값을 할당(참조)한다는 것입니다.

이러한 차이가 어떠한 의미를 지니는지 왜 이런 차이가 발생하는지를 코드가 실제 메모리에 저장되는 형태를 통해 알아보도록 하겠습니다.

 

기본형(Primitive Type)

js
var a;
a = 10;

var b = 'abc';
b = false;

var c = b;
// b === c

c = 20;
// b !== c

참고로 아래의 그림들은 실제 메모리를 나타낼 수 없기에 메모리를 추상화한 그림입니다.

변수명과 주소로 된 표가 있고 주소와 데이터로 구성된 표가 있는데 이 두개는 별개의 저장공간을 가지는 것이 아니라 편의상 흐름도 파악을 위해 표기한 것입니다.

 

var a;

변수 a 를 선언하면 컴퓨터는 우선 메모리 안에 데이터를 담을 공간을 미리 확보합니다.
그림에서는 임의로 아래 표의 주소 313번 공간을 확보했습니다.


 

그 다음으로 확보한 주소값을 변수명 a 와 매칭시킵니다.


 

a = 10;

이후 코드 진행중에 a 에 10을 할당하라는 명령을 만나면 컴퓨터는 변수 a를 찾아서 변수 a와 매칭되어 있는 주소값인 @313번을 읽고 그 주소로 이동해서 그 공간에 10을 집어 넣습니다.


 

var b = 'abc';

이번에는 변수 b 를 선언하고 문자열 abc 를 할당하라는 명령어(var b = 'abc')를 내려봅니다.
우선 데이터가 담길 314번의 공간을 확보하고 확보된 공간의 주소값을 가지고 변수명 b 와 주소 @314에 매칭시킵니다.


그리고 다시매칭된 주소의 314번으로 이동해서 문자열 'abc'를 저장합니다.
즉, var b = 'abc' 라는 문장은 앞서 실행했던 var a; a = 10; 를 하나로 합쳐놓은 문장에 불과합니다.


 

이러한 것을 선언할당이라고 합니다.

  • 선언 과정 : 공간을 확보하고 변수명과 주소를 매칭시키는 과정
  • 할당 과정 : 해당 변수가 가리키는 주소의 공간에 데이터를 저장하는 과정

 

b = false;

그 다음 문장에서 다시 b에 false 를 할당(b = false)하라고 명령하면 false 를 들고있는 채(var 라는 키워드가 없기 때문에)로 현재 가지고 있는 변수명들 중에 b 가 있는 지를 검색합니다. 만약 b 를 찾지 못하면 앞서와 마찬가지로 선언 과정을 거치게 됩니다. b 는 현재 메모리상에 존재하기 때문에 b 가 가리키는 주소(@314)값을 읽어서 해당 주소위치로 이동한 다음 그 자리에 false 를 덮어쓰게 됩니다.


 

var c = b;

그리고 다음 문장에 var c = b; 을 수행하면 먼저 var 키워드가 있으니 선언과정부터 거치게 됩니다.
빈 공간인 315번을 확보하고 그 주소를 변수 c 와 매칭시킵니다. 그리고 314번으로 이동하여 b값인 false 값을 읽어옵니다.
그리고 읽어들인 값 false 를 가지고 c 를 찾아서 c가 가리키는 315번 공간에 false 를 넣게됩니다.


여기서 중요한 것은 이렇게 저장된 기본형 데이터들은 그 자체로 비교가 가능하다는 것입니다.

b 가 가리키는 메모리 공간상에 저장된 false 와 c 가 가리키는 메모리 공간상에 저장된 false 는 완전히 같은 값입니다.
false 라는 명칭 자체는 세상에 단 하나뿐인 키워드입니다. 우리는 이 하나뿐인 단어를 이곳저곳에서 사용하고 있는 것입니다.
하나뿐인 단어를 서로 비교하려고 하니까 당연히 완전히 같다(b === c)고 볼 수 밖에 없습니다.

 

c = 20;

다시 c 에 20을 할당하라(c = 20)고 하면 20을 가지고 c 가 저장된 메모리 공간을 찾아서 원래있던 false 대신 20을 넣습니다.
그런데 이 동작은 false 란 값이 20 으로 변하는 것이 아니라 메모리 공간에 있던 false 대신 20이란 값을 덮어씌워 버리는 것입니다.
이제 b 가 가리키는 곳의 값인 false 와 c 가 가리키는 공간의 값인 20 은 서로 완전히 다른 값이 되었으므로 b 와 c 는 같지않다(b !== c)라는 조건이 성립합니다.


 

 

 

참조형(Reference Type)

참조형 데이터 중 가장 대표적인 객체를 살펴보도록 하겠습니다.

js
var obj = {
    a : 1,
    b : 'b'
};

var obj2 = obj;
obj2.a = 10;
console.log(obj2.a); // 10
console.log(obj.a); // 10

 

기본형 타입과 마찬가지로 메모리 공간을 확보하고 주소를 변수명과 매칭시키는 과정은 동일합니다.


 

다음으로 할당과정을 할 차례인데 할당을 하려고 보니 그 값이 기본형이 아니고 참조형입니다.
참조형 데이터들은 프로퍼티(property)데이터(data), 즉 key : value로 묶인 쌍들로 이루어져 있습니다. 프로퍼티명은 변수와 비슷한 성질을 지니고 있고 변수명과 실제 데이터는 주소값을 통하여 연결되어 있는데 프로퍼티와 데이터 사이에서도 같은 동작을 합니다.
우선 각 프로퍼티명과 value 가 담길 주소를 매칭하기 위해서 공간을 새로 확보합니다.
여기선 새로 임의로 1011번에 공간을 확보합니다.


 

그리고 나서 1012번에 a 프로퍼티의 value 가 담길 공간을 확보하고 확보된 주소를 a 프로퍼티와 매칭시킵니다.


그리고 1013번에 b 프로퍼티의 value 가 담길 공간을 확보하고 그 할당된 주소의 1013번을 b 프로퍼티와 매칭시킵니다.


 

이제 다시 각각의 주소값마다 기본형 데이터값을 할당하게 됩니다.
1012번에는 1 을 1013번에는 'b' 가 저장됩니다.


 

이제 앞서 어딘가에 저장되어 있을 객제 저장 정보의 주소 즉, 여기서는 1011번을 413번에 매칭시키게 됩니다.


이러한 과정을 데이터 공간의 기본형 데이터가 담길 때까지 반복하게 됩니다.
즉, 참조형 데이터는 결과적으로 기본형 데이터들의 집합이라고 볼 수 있습니다.

 

var obj2 = obj;

그렇다면 이 상태에서 obj2 에 obj 를 할당하라고 하면 어떠한 일이 벌어질까요?
414번에 공간을 확보하고 obj2 와 매칭시킨 다음 obj 가 가리키고 있는 데이터인 1011번이라고 하는 주소를 obj2 에 할당하게 됩니다.


이것이 바로 참조가 이루어지는 형식입니다.
객체는 어딘가에 따로 저장되어 있는데 그객체가 저장된 주소만을 복사해 온 것입니다.

 

obj2.a = 10;

그래서 obj2 의 a 프로퍼티(obj.a)에 10을 할당(obj2.a = 10;)하라고 하면 obj2 에 매칭된 414번 주소로 가서 다시 1011번으로 이동한 다음 1011번 안에서 a 프로퍼티를 찾고 a 와 매칭되어 있는 1012번 주소로 이동하여 1 대신에 10 을 넣게됩니다.


여기서 당연하게도 obj2.a 값은 10이 나오는데 원본인 obj.a 값도 마찬가지로 10 이 출력됩니다.

obj2 객체가 obj 객체와 다른 새로운 객체를 만든 것이 아니라 본래 obj 가 바라보던 객체를 함께 바라보고 있기 때문입니다. 즉, obj === obj2 는 완벽히 동일한 객체를 참조한다고 말합니다.
그리고 이를 같은 곳을 바라보고 있다고 하여 포인터(pointer)가 같다 혹은 같은 포인터를 바라본다고 표현하기도 합니다.

지금까지의 과정이 기본형 데이터와 참조형 데이터의 가장 큰 차이점이고 이 원리를 이해해야만 이후에 접할게 될 얕은 복사, 깊은 복사 개념들이 등장하는 이유를 이해하실 수 있게 됩니다.

 

중첩된 객체 타입

이번에는 중첩된(nested) 객체를 살펴보도록 하겠습니다.
참고로 여기서 말하는 중첩된 객체는 참조형 데이터 안에 참조형 데이터가 있는 경우들을 nested, 즉 중첩되어 있다고 표현합니다.

js
var obj3 = {
    a : [4, 5, 6]
};

obj3.a = 'new';

 

공간을 확보하고 선언과정은 위에서 살펴본 것과 같이 동일합니다.


 

해당 표에서는 1184번을 확보하고 확보된 해당 공간에 프로퍼티별로 저장될 공간을 다시 확보하게 됩니다.
예제 코드에서는 a 프로퍼티 하나뿐이므로 1185번을 a 프로퍼티가 저장될 공간으로 삼도록 하겠습니다.


 

이제 1185번에 a 프로퍼티의 value 를 할당해야 하는데 할당하려고 보니 그 값이 인덱스로 이루어진 value 값들의 집합인 배열입니다.
그래서 1326 ~ 1328번까지 공간을 다시 확보하고 1185번에는 확보된 공간의 주소값들로 이루어진 배열이 넣어지게 됩니다.


 

위와 같은 과정을 거친 후에 각 주소마다 value 들을 대입하게 됩니다.
이번에는 요소들이 모두 기본형 데이터이기 때문에 더이상의 공간 확보 작업을 하지 않을 것입니다.


 

이렇게 대입을 모두 마치고 나면 이제 모든 데이터들에 대한 주소값 연결이 완료된 객체의 주소를 547번에 매칭시키는 것으로 obj3 의 메모리 할당 작업이 마무리됩니다.


 

obj.a = 'new';

그런데 만약 이 상황에서 'new' 라고 하는 문자열인 기본형 데이터를 obj3.a 에 할당하라고 하면 어떻게 될까요?
배열 정보에 있던 자리에 순수 데이터인 new 문장열이 들어옴으로써 1326 ~ 1328번까지 저장되어 있던 데이터들은 링크가 사라지고 맙니다. 즉, 주소를 참조하고 있는 곳이 어디에도 없게 되게 되는 것입니다. 이들은 가비지 콜렉터라고 불리는 메모리 청소기인 청소 대상에서 언젠가 쥐도새도 모르게 사라지게 됩니다.



 

 

Jaehee's WebClub