Skip to content
On this page

변수선언과 데이터

주소값을 가지는 메모리 공간에 데이터를 저장하려면 변수 선언과 할당과정이 필요합니다.

JS에서는 C/C++, Java처럼 변수명(주소값 대신 사용되는 memory id)으로 구분되는 공간에 바로 데이터를 저장하지 않습니다.

대신 실제 데이터가 할당되는 메모리 공간인 "데이터 영역"과 이 메모리 공간을 가리키는 주소값을 저장하는 "변수영역"이 존재합니다.

변수 선언

js
var a;
console.log(a); // undefined

위와 같이 선언만 한다면 메모리는 다음과 같은 상태를 가집니다.

변수 영역

주소...10031004...
데이터변수명: a, 주소값: (null)

할당된 데이터가 없다는 것을 표시하기 위해 JS 엔진은 해당 변수에 접근할 때 undefined를 반환합니다. 또한 선언된 변수가 참조하는 주솟값은 null을 가집니다.

데이터 할당

js
var a;
a = "abc";

var b = "bbb";

변수를 선언한 뒤에 데이터를 할당하면 다음과 같은 메모리 상태가 됩니다.

변수 영역

주소...10031004...
데이터변수명: a, 주소값: @5004

데이터 영역

주소...50035004...
데이터‘abc’

JS에서 변수영역과 분리된 공간에 데이터를 저장하는 이유는 메모리 공간을 효율적으로 관리하기 위해서입니다.

만일 변수영역에서만 데이터를 저장한다면 하나의 변수에 할당된 데이터가 변환될 때마다 공간의 크기를 유동적으로 늘리거나 줄이는 작업에 의해서 자원낭비가 발생합니다.

변수에 할당할 데이터가 달라질 때마다 분리된 영역에 데이터를 저장하고 그 주솟값만 변수에 저장하면 기존의 불필요한 연산을 줄일 수 있습니다.

더 이상 참조되지 않는 데이터는 GC에 의해서 해당 공간이 회수됩니다.

또 다른 효과로는 중복된 데이터를 서로 다른 변수에 저장할 때 필요한 공간의 크기가 줄어드는 겁니다.

가령 500개의 변수에 동일한 5를 할당한다고 합시다.

그럼 변수마다 변수영역 공간(8B * 500 = 4KB)을 할당하는 방식과 데이터 5는 데이터 영역에 한번만 저장하고 그 주소값만 변수들에게 할당하는 방식(500 * 2B + 8 = 1008B) 중 후자가 절약할 수 있는 공간의 크기가 많음을 알 수 있습니다.

INFO

책에서는 주소값이 2B를 차지한다고 가정합니다.

정리하자면 변수를 선언하고 데이터를 할당하는 방식은 2가지가 있습니다.

하나는 데이터 영역에 새로운 공간을 만들거나 중복된 데이터가 저장된 공간의 주솟값을 변수영역 메모리 공간에 저장하는 겁니다.

JS 엔진은 후자, 전자 순으로 가능여부를 따집니다.

primitive, reference

변수(variable)과 상수(constant)를 구분짓는 것은 변수영역 메모리 공간에 데이터가 저장된 주솟값을 재할당할 수 있는지 여부로 구분합니다.

primitive 데이터와 reference 데이터를 구분짓는 것은 불변성(immutablity) 즉, 데이터 영역 메모리에서의 재할당 가능여부입니다.

primitivereference
불변성 여부

primitive 데이터는 한번 데이터 영역 메모리에 할당되면 동일한 공간에 다른 데이터가 할당되지 않고 동일한 데이터를 사용한다면 해당 주솟값만 사용하기 때문에 불변성을 가집니다.

reference 데이터는 primitive 데이터와 다르게 가변성을 가지는데 애초에 메모리에 저장되는 방식이 다르기 때문입니다.

js
var obj1 = {
  a: 1,
  b: "abc",
};

obj1.a = 4;

위와 같은 객체 obj1은 다음과 같은 메모리 구성을 갖습니다.

변수 영역

주소10011002...
데이터변수명: obj1, 주소값: @5001

데이터 영역

주소5001500250035004...
데이터주솟값: @7103~?1‘abc’

객체 obj1의 속성 영역

주소71037104...
데이터속성명: a, 주소값: @5003속성명: b, 주솟값: @5004

위에서 볼 수 있듯이, 객체는 속성들을 저장하기 위한 별개의 변수영역을 가지고 있지만 데이터 영역에는 여전히 primitive 데이터를 저장합니다.

만일 속성 a의 값을 다르게 할당해도 실제로 변하는 부분은 속성 a의 변수에 저장되는 주소값이고 실제 변수 obj1이 가리키는 데이터 메모리 주소인 @5001은 변하지 않습니다.

정리하자면, 속성에 다른 데이터를 할당하기 위해서 primitive 데이터처럼 새로운 객체를 생성하지 않는 점이 가변성의 특징으로 볼 수 있습니다😎

reference 데이터의 가변성은 복사(?)된 변수에 있어서 primitive 데이터와 큰 차이점을 보입니다.

js
var a = 10;
var b = a;

var obj1 = { c: 10, d: "ddd" };
var obj2 = obj1;

b = 15; // a !== b
obj2.c = 99; // obj1 === obj2

위와 같이 변수를 복사한 뒤 새로운 데이터를 변수 또는 속성에 할당하면 변수 b로의 할당은 변수 a에 전혀 영향을 주지 않는 반면에, obj2의 속성 c로의 할당은 원본 객체인 obj1에도 영향을 줍니다.

TIP

참고로 객체의 속성들을 저장하기 위한 메모리 공간(객체 변수 + 데이터)은 속성이 있는 경우에만 동적으로 생성되고 할당됩니다.

immutable object

객체는 가변성을 가지기 때문에 변수에 복사를 한 뒤에 속성을 다르게 할당하면 원본 데이터에 영향을 준다는 특징이 있습니다.

하지만 원본 객체에 영향을 주지 않는 불변객체(immutable object)를 사용한다면 안심하고 변경할 수 있고 이는 많은 함수형 프로그래밍, 디자인 패턴, 프론트 프레임워크 등에서 중요한 개념입니다.

불변객체는 객체의 필드의 값을 변경할 때마다 새로운 객체가 생성되기 때문에 원본이 존재하지 않습니다.

불변객체를 만드는 방법으로는 다음과 같습니다.

  • ES6의 spread syntax : {...obj}
  • Object.assign 메서드
  • Immutable.js, baobab 등 라이브러리

shallow-copy, deep-copy

객체를 복사하는 방법은 shallow-copy, deep-copy 2가지가 있습니다.

shallow-copy 방식은 객체의 own-property들의 주소값만 새로운 속성으로 복사하기에 own-property가 primitive 데이터라면 별개의 속성을 만들지만 reference 데이터라면 가변성에 의해서 원본 속성을 참조합니다.

그래서 own-property가 객체인 경우, 해당 객체 자체와 내부의 모든 속성들까지 별개의 데이터 영역에 복사하려면 deep-copy가 필요합니다.

deep-copy는 다음과 같은 방법으로 수행할 수 있습니다.

shallow-copy는 ES6의 spread syntax나 Object.assign 메서드 등으로 구현할 수 있습니다.