JS-Data

5 minute read

Introduction

우리가 프로그래밍을 통해 하고자 하는 것은 대부분 데이터를 처리하는 일이다. 데이터를 효과적으로 처리하기 위해 여러가지 자료구조, 알고리즘등이 있지만 무엇보다 해당 언어가 메모리 영역에서 데이터를 어떻게 다루는 지에 대한 이해가 필수적이다. 따라서 JS 시리즈의 가장 첫 번째 글로 자바스크립트가 데이터를 메모리 영역에서 어떻게 처리하는 지에 관해 다루어 보려고한다.

Data Type

자바스크립트의 데이터 타입은 다음과 같이 크게 두 가지로 나뉜다.

  • Primitive type
  • Reference type

Primitive type에는 숫자, 문자, 불리언, null, undefined, (ES6에 추가된 심볼) 등이 있으며, Reference type에는 객체, 배열, 함수, 날짜, (ES6에 추가된 Map, Set등) 가 있다.

javascript에서 Primitive type과 Reference type을 나누는 기준으로는 메모리에 데이터 이 써져있냐, 아니면 실제 값이 존재하는 주소가 써있냐라고 생각한다. 필자 또한 제대로 공부해보기 전까지는 그렇게 알고 있었지만 엄밀하게 말하자면 둘 다 메모리의 주소가 써있다.

그렇다면 자바스크립트에서 실제로 변수의 선언과 할당 시에 메모리 영역에서 무슨 일이 일어나는지 알아보자.

또한 Primitive type은 불변성을 띈다고 하는데 과연 어떻게 불변성이 유지되는지, 가변성이라고 하는 것은 어떻게 나타나는지 알아보자.

Primitive type의 선언과 할당

01 var a = 'abc';

01번 라인을 수행하면 메모리에서는 어떤 일이 벌어질까?

  1. 먼저 메모리 영역에 빈 공간을 확보하고,
  2. 그 공간에 ‘a’라는 이름을 붙이고,
  3. 그 곳에 ‘abc’라는 문자열을 할당한다.

라고 생각할 수 있겠지만, 앞서 말했던 것처럼 자바스크립트는 이런 식으로 동작하지 않는다.

‘abc’라는 문자열을 직접 저장하지 않고, 별도의 메모리 공간을 확보해 그곳에 ‘abc’라는 문자열을 저장하고, 그 주소를 변수 a에 저장한다.

이런 자바스크립트의 특성을 나타내기 위해 메모리를 ‘변수 영역’, ‘데이터 영역’으로 구분지어 부르겠습니다. 이는 표준용어가 아니며 ‘코어 자바스크립트’책의 표현을 인용한 것입니다.

실제 01번 라인이 수행될 때, 데이터 할당의 흐름은 아래 그림을 보면서 따라가 보자.

data1-1

  1. 변수 영역에 빈 공간(@1003)을 확보한다.
  2. 확보한 공간의 식별자를 a로 지정한다.
  3. 데이터 영역의 빈 공간(@5004)에 문자열 ‘abc’를 저장한다.
  4. 변수 영역에서 a라는 식별자를 검색한다(@1003)
  5. 앞서 저장한 문자열의 주소(@5004)를 @1003의 공간에 대입한다.

그렇다면 왜 변수 영역에 직접 값을 대입하지 않고, 굳이 주소를 저장하여 한 단계 더 거치는 것일까요?

그 이유는 데이터 변환을 자유롭게 할 수 있게 함과 동시에 메모리를 더욱 효율적으로 관리하기 위함 이라고 합니다.

자바스크립트는 C나 JAVA 등과 같은 타입 기반의 언어와 달리 변수의 선언시에 타입을 지정해 주지않는다. 그 말은 해당 변수는 언제든 다른 타입으로 변할 수 있는 상태가 되야한다는 뜻이다.

예를 들어 변수 영역에 직접 값을 대입한다고 가정해보자.

  1. 변수 영역에 빈 공간(@1003)을 확보한다.
  2. 확보한 공간의 식별자를 a로 지정한다.
  3. 변수 영역에서 a라는 식별자를 검색한다(@1003)
  4. @1003의 공간에 ‘abc’를 대입한다.

아마 01번 라인을 실행했을 때 위와 같은 흐름으로 진행될 것이다.

그런데 여기서 아래와 같이 a의 값을 5로 바꾸어준다면 어떤 일이 벌어질까?

a = 5;

아마도 @1003의 공간에 정수 5를 대입할 것이다.

  • 그런데 여기서 짚고 넘어가야할 부분이 있다. 문자열 ‘abc’와 정수 5는 타입이 다르기 때문에 저장하기위해 필요한 메모리 크기도 다르다. 위 그림에서는 이를 고려하지 않고 그렸지만 사실 중요한 문제다.

자바스크립트에서 문자는 1바이트 숫자는 8바이트의 공간이 필요하다. 따라서 a=5를 실행할 때 변수 a의 크기도 함께 변해야 한다.(3바이트 -> 8바이트) 더 심각한 상황은 만약 @1004의 공간을 다른 변수가 차지하고 있다면, 즉 메모리의 뒤쪽으로 크기를 늘려갈 수 없다면 메모리 상에서 비어있는 8바이트짜리 크기를 찾아낸 다음 그곳에 다시 변수 할당, 대입의 과정을 진행해야한다. 이는 무척이나 비효율적인 작업이다.

따라서 자바스크립트는 변수와 데이터를 별도의 공간에 나누어 저장하는 방식을 선택한 것입니다.

Primitive type의 불변성

01 var a = 'abc';
02 a = a + 'def';

04 var b = 5;
05 var c = 5;
06 b = 7;

이전에 01번라인까지 실행한 흐름을 확인했었다. 그 이후 02번 라인을 수행하게되면 어떻게 될까?

@5004의 값이 ‘abcdef’로 변경될 것 같지만 실제로는 그렇지 않다. 자바스크립트는 새로운 문자열 ‘abcdef’를 만들고, 그 주소를 변수 a에 저장한다. ‘abc’와 ‘abcdef’는 완전히 별개의 데이터가 된 것이다.

data1-2

또한 데이터영역에 이미 존재하는 값이라면 새로 만들지 않고, 이미 존재하는 값을 재활용 한다. 이를 위의 코드가 실행되면서 일어나는 일을 살펴보며 알아보자.

04번 라인을에서 변수 b에 숫자 5를 할당한다.

  1. 데이터 영역에서 5를 찾는다.
  2. 없다면 데이터 공간을 하나 만들어 5를 저장한다.
  3. 그 주소를 b에 저장한다.

이어서 05번 라인이 실행되면

  1. 데이터 영역에서 5를 찾는다.
  2. 이미 존재하기 떄문에 그 주소를 c에 저장한다(재활용)

06번 라인에서 변수 b의 값을 7로 바꾸려고한다. 그럼 기존에 저장된 5를 7로 바꾸는 것이 아니고, 기존에 7이라는 값이 있다면 재활용하고, 없다면 새로 만들어서 b에 저장하게 된다.

결국 5와 7 모두 다른 값으로 변경할 수 없다.

이처럼 문자열이든 숫자든 Primitive type의 데이터는 값이 변경되지 않는다. 변경되는 것은 변수 영역에서의 참조하고 있는 주소값이다. 이것이 바로 Primitive type에서의 불변성이다. 한 번 만들어진 값은 가비지 컬렉팅을 당하지 않는 한 영원히 변하지 않는다.

Reference type의 선언과 할당

Reference type은 어떻게 선언과 할당이 이루어질까? Primitive type에서와 같은 방식으로 살펴보자.

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

data1-2

  1. 변수 영역에 빈 공간(@1002)를 확보하고, 그 주소의 이름을 obj라고 지정한다.
  2. 임의의 데이터 저장공간 (@5001)에 데이터를 저장하려고 보니 여러 개의 프로퍼티로 이루어진 데이터 그룹이다. 이 그룹 내부의 프로퍼티들을 저장하기 위해 별도의 변수 영역을 마련하고, 그 영역의 주소(@7103 ~ ?)를 @5001에 저장한다.
  3. @7103 및 @7104에 각각 a와 b라는 프로퍼티 이름을 지정한다.
  4. 데이터 영역에서 1을 찾고, 없으므로 @5003에 1을 저장하고 그 주소를 @7001에 저장한다. 문자열 ‘bbb’역시 없기 때문에 @5004에 저장하고 그 주소를 @7104에 저장한다.

전체적인 흐름에서 Primitive type과 유사하긴 하지만 obj라는 변수에 할당된 주소 값이 실제 값이 있는 주소가 아니고, 실제 값들이 있는 주소라는 점에서 차이가 있다.

C나 C++언어를 아는 독자라면 Primitive type은 포인터의 느낌이고, Reference type은 이중 포인터의 느낌이다.

Reference type의 가변성

Primitive type은 모두 불변값이다. 그렇다면 Reference type은 모두 가변값일까? 대부분의 경우에는 가변값이 맞지만, 꼭 그렇지만은 않은 경우도 있다. 변경 불가능하게 설정할 수도 있고, 아예 불변값으로 활용할 수도 있다. 하지만 우선은 Reference type은 가변적이다라고 생각해보자.

var obj = {
    a: 1,
    b: 'bbb'
};
obj.a = 2;

앞서서 obj객체의 선언과 할당까지 알아보았다. 여기서 obj의 특정 프로퍼티의 값을 변경하면 어떻게 될까?

data2-2

  1. obj의 a프로퍼티에 2를 할당하려고 2를 찾는다.
  2. 2가 없으므로 @5005에 2를 저장한다.
  3. 그 주소를 @7103에 저장한다.

마지막 라인의 수행 결과와 그 전을 비교해 보면 obj가 바라보는 주소는 변하지 않았다. 즉, 새로운 obj라는 객체가 만들어진 것이 아니고 기존의 obj라는 객체가 변경되었다는 뜻이다. 이 점이 Primitive type과는 다른 Reference type의 가변적인 특징이다.

Garbage Collector

그러면 @5004의 ‘bbb’는 어떻게 될까요? 위에서 잠깐 언급한적이 있지만 바로 가비지 컬렉터에 의해 수거됩니다. obj.a = 2 에 의해서 a변수가 바라보는 값이 @5004에서 @5005로 바뀌게 되었다. 결과적으로 @5004는 더이상 자신의 주소를 참조하는 변수가 없게 되는 것이다. 어떤 데이터의 대해 자신의 주소를 참조하는 변수의 개수를 참조 카운트라고 한다. 참조카운트가 0이된 메모리 주소는 가비지 컬렉터가 수거해간다.

즉, @5004의 참조 카운터는 obj.a = 2 가 실행되면서 1에서 0으로 바뀌었고, 가비지 컬렉터의 대상이되어 수거되어 해당 메모리는 비워지게 된다.

END

지금까지 자바스크립트에서 데이터를 저장할때 메모리 영역에서 어떤 일이 일어나는 지 살펴보았다. 익숙하게 사용하던 자바스크립트였지만 막상 변수가 어떻게 선언되고 할당되는 지도 잘 몰랐었지만 이번에 공부해보면서 확실히 알게되고, 새롭게 알게 되는 부분이 많아서 재밌었다.

자바스크립트의 Reference type이 가변적인 특징을 가지고 있기 때문에 다룰 때 주의해야한다. 잘못 다루었다가는 값이 변하면 안되는 객체의 값을 변경해버릴 수도 있기 때문이다. 따라서 다음 포스팅에서는 자바스크립트에서 변수가 복사될 때 어떻게 되는지, 변하지 않는 불변객체를 만드려면 어떻게 해야되는지에 대하 다뤄보도록 하겠다.

Reference

정재남(2020), 코어 자바스크립트, 위키북스, pp.01-15

Leave a comment