By kimcoder
2022.02.19

자바스크립트의 데이터 타입과 메모리

자바스크립트는 느슨한 타입(loosely typed)의 동적(dynamic) 언어이다.
언어에서 사용할 수 있는 데이터 타입의 종류와 몇가지 특징들을 알아보자.

데이터 타입 종류

자바스크립트의 데이터 타입은 크게 2가지로 나눌 수 있다.

  • 원시형 타입(Primitive type)
  • 참조형 타입(Reference type)

위의 2가지를 나누는 기준은 할당이나 연산시 값을 복제하냐 혹은 참조하냐라고 볼 수 있겠다.
엄격하게 말하면 사실 둘 다 복제를 하지만, 복제를 하는 대상이 다르다.
이는 아래에서 조금씩 더 다뤄보도록 하겠다.

원시형

원시형으로 다뤄지는 타입들은 아래와 같다.
Number, String, Boolean, null, undefined, Symbol, BigInt
이들은 일반적으로 불변성을 가지는 데이터 타입이라고 표현된다.

원시형 타입의 몇 가지의 특징을 살펴보자.

특징

  • 값 자체는 불변하며 변형이 불가능 하다.
  • 값은 고정된 크기로 메모리에 저장된다. (ex) 숫자의 경우 64비트)
  • 같은 값을 사용한다면 하나의 메모리를 사용한다.

원시형 타입과 메모리

원시형 타입 변수의 할당과 연산시, 메모리의 변화를 파악해보자.

let name = '김대현'; // 변수 선언과 값 할당
name = 'kimcoder'; // 변수 값 재할당

위의 코드에서 name이라는 변수 선언시에는 메모리에서 2개의 주소를 할당한다.
하나는 변수 식별자(변수명)을 위한 것이고, 하나는 변수 값(데이터)를 위한 것이다.

변수 영역주소100210031004...
데이터이름 : name
값: @5003
데이터 영역주소500250035004...
데이터'김대현'

1003의 식별자는 name이고, 데이터는 김대현이라는 값의 메모리 상 주소(@5003)이다.

이제, 변수 값을 kimcoder로 재할당시 변화를 확인해 보자.

변수 영역주소100210031004...
데이터이름 : name
값: @5004
데이터 영역주소500250035004...
데이터'김대현''kimcoder'

console.log(name)으로 확인해보면 kimcoder가 출력되므로 의도한대로 변수 값의 변화가 되었다고 보인다!
하지만 실제 @5003에 저장된 문자열 값의 변화는 일어나지 않았다. 단지 변수 name의 값(메모리 주소)이 @5003 에서 @5004으로 변한 것이다. name에, kimcoder라는 값을 할당하기 전에 이미 kimcoder라는 값을 사용하여서 메모리에 저장이 되어있는 상황이라면 그 주소로 변하게 된다.

만약, 김대현이란 값을 더 이상 사용하지 않게되면, 가바지 콜렉션의 대상이 된다.

참조형

참조형은 포괄적으로 보자면 모든 Object라고 볼 수 있는데,
여기에는 Array, Function, Date, RegExp, Map, WeakMap, Set, WeakSet 등이 있다.
원시형이 아닌 타입들은 모두 참조형이라고 생각하여도 좋다.

특징

  • 참조형(객체)는 키와 값으로 구성된 프로퍼티들의 집합이다.
    • 프로퍼티 값으로는 자바스크립트의 모든 값을 사용할 수 있다.
  • 변수에 값을 할당할 때, 값이 아닌 데이터 객체의 주소를 저장한다.
  • 고정된 크기의 메모리를 사용하지 않는다.

참조형 타입 변수의 메모리 구조

참조형도 데이터를 변수에 할당하는 과정이 메모리상에서 어떻게 처리되는지 확인해보자.

let data = {
  name: 'kimcoder',
  nation: 'korea',
};
변수 영역주소1002100310041005...
데이터이름 : data
값: @5002
데이터 영역주소5002500350045005...
데이터@7002 ~ ?'kimcoder''korea'
객체 @5002의 변수 영역주소7002700370047005...
데이터이름: name
값: @5004
이름: nation
값: @5005

원시형 데이터와의 차이는 객체의 변수(프로퍼티) 영역이 별도로 존재한다는 것이다. 객체가 별도로 할애한 영역은 변수 영역일 뿐 데이터 영역은 기존의 메모리 공간을 그대로 활용 하고 있다. 또한, 데이터 영역에 저장된 값은 모두 불변값이다(=원시형).
그러나 변수에는 다른 값을 얼마든지 대입할 수 있으며, 이러한 성질 때문에 흔히 참조형은 불변하지 않다라고 표현하는 것이다.

객체 내 요소의 값 변경시 어떤 변화가 일어나는지도 확인해 보자.

let data = {
  name: 'kimcoder',
  nation: 'korea',
};

data.name = '김대현';
변수 영역주소1002100310041005...
데이터이름 : data
값: @5002
데이터 영역주소5002500350045005...
데이터@7002 ~ ?'김대현''kimcoder''korea'
객체 @5002의 변수 영역주소7002700370047005...
데이터이름: name
값: @5003
이름: nation
값: @5005

변화된 영역을 살펴보자. 변수 data가 바라보고 있는 주소는 @5002로 변하지 않았다. 다만, 객체 내부 요소의 값만 변경이 되었다.
객체 내부의 요소는 추가 및 삭제, 수정이 가능하기 때문에 참조형 타입은 가변값이라고 표현한다.

참조형 객체 복사

let otherData = data;

위와 같은 코드가 있다고 생각해보자

변수 영역주소1002100310041005...
데이터이름 : data
값: @5002
이름 : otherData
값: @5002
데이터 영역주소5002500350045005...
데이터@7002 ~ ?'김대현''kimcoder''korea'
객체 @5002의 변수 영역주소7002700370047005...
데이터이름: name
값: @5003
이름: nation
값: @5005

otherData를 위해 변수 영역에 추가로 공간이 할당되었고, 데이터는 그대로 @5002 객체를 바라보고 있다.
따라서 원본 dataname 속성 값을 변경하면 otherDataname 속성 값도 같이 변하게 된다.

위와 같은 성질로 인해 참조형 데이터를 사용할 때, 불변성을 유지하고자 추가적인 처리를 해주는 것이다.

대표적인 예시의 상황으로 함수 인자로 전달하고 참조시, 원본 데이터의 값 변경을 막기 위해 추가 처리를 한다.

let data = {
  name: 'kimcoder',
};

data = {
  age: '12',
};

위와 같이 객체의 프로퍼티가 변경되는 것이 아니라 변수의 데이터 자체가 변경되는 경우에는 해당되지 않는다.

마치며

원시형 타입은 값을 변경할 때 값 자체가 변경되는게 아닌 변수영역의 메모리가 변경되는 것이다.
참조형 타입은 원시형 타입과 달리 변수의 데이터가 프로퍼티의 집합으로 구성되어 있다.
내부 프로퍼티의 값의 변경이 일어날 데이터 객체는 변경되지 않은 상태로 내부 프로퍼티만 변경된다.
사용할 때는 별도의 불변성 처리를 위한 코드를 작성해야 하는 것이 좋다.

References