💡 요즘 매주 화요일 마다 리액트 스터디에 참여하고 있는데, 여기서 정리하게 된 JavaScript의 변수메모리 구조 대해서 좀 더 자세히 정리해보고자 한다.
🙌 이 게시글에서는 여러 언어를 동시에 다루므로, 가벼운 내용만 다루기 때문에, 각 언어에 대해서 심화 공부를 하신 분께서는 약간 틀린 내용을 발견하시게 된다면, 언제든 의견 바랍니다.

  • 공부한 내용을 기억하기 위해 적어놓는 글입니다.

컴퓨터 프로그래밍에서의 변수

일단 자바스크립트의 변수에 대해 알기 전에, 컴퓨터 프로그래밍에서의 변수에 대해서 알고 넘어가자.

Variable 이라는 단어의 사전적 의미는 다음과 같다.

1. 변하기 쉬운
1. 변하게 할 수 있는
// Oxford Languages 참조

또한 WikiPidiaVariable(Computer Science)를 다음과 같이 설명한다.

In computer programming, a variable is an abstract storage location paired with an associated symbolic name, which contains some known or unknown quantity of information referred to as a value; or in simpler terms, a variable is a named container for a particular set of bits or type of data (like integer, float, string etc...). A variable can eventually be associated with or identified by a memory address.

아래는 DeepL을 사용하여 위 내용을 번역 한 내용이다.

컴퓨터 프로그래밍에서 변수는 값이라고 하는 알려진 또는 알려지지 않은 양의 정보를 포함하는 연관된 기호 이름과 짝을 이루는 추상적인 저장 위치입니다. 또는 더 간단히 말하면, 변수는 특정 비트 집합 또는 데이터 유형(정수, 부동 소수점, 문자열 등)을 위한 명명된 컨테이너입니다. 변수는 결국 메모리 주소와 연관되거나 메모리 주소로 식별될 수 있습니다.

컴퓨터 프로그래밍에서 변수는 값이라고 하는 알려진 혹은 알려지지 않은 양의 정보를 포함하는 연관된 기호 이름짝을 이루는 추상적인 저장위치라고 설명이 적혀있다.
중요한 부분을 강조 표시 하였다. 이 부분에서 변수에 대한 정확한 정의를 얻어낼 수 있다.
즉 변수는 알려진 혹은 알려지지 않은 양의 정보를 가지고 있으며, 연관된 기호 이름과 짝을 이루는, 추상적인 저장위치이다. 그리고 이 저장위치는 보통 우리가 흔히 혹은 메모리이라고 부르는 주 기억장치이다.

아래 간단한 C언어 코드 1줄을 준비했다.

int myAge = 23;

이 코드에서 알 수 있는 사실은 이 변수는 myAge 이라는 이름을 가지고 있고, 그 값은 23 이라는 이며, 이 변수의 자료형태int정수형이다.
좀 더 길게 써보면, 위 코드를 읽은 컴퓨터메모리Stack영역에서 4Byte공간을 확보하고, 그 공간에 23(DEC) 이라는 값을 초기화 하라는 명령을 실행한다. 여기서 확보 된 공간이 바로 변수이다. 실제로는 좀 더 있지만 생략 그런데 이 설명 중 myAge라는 변수의 이름integer라는 변수의 자료형이 설명에서 빠져있다. 왜냐면 위 설명은 CPU 명령을 기준으로 설명한 내용이다. 그렇다면 myAge란 이름은 어디로 사라졌을까?


변수와 메모리의 관계

프로그램에서 변수 이름이 동작하는 과정에 앞서 먼저 변수와 메모리의 관계에 대해서 다시 되짚어 보자.
앞서, 우리는 이전 주제를 통해, 변수메모리에 확보 된 어떠한 정보를 포함하기 위한, 공간임을 알 수 있었다.

변수 선언문

그렇다면 우리가 변수를 선언 할 때, 얻을 수 있는 정보와 그 정보를 한 번 분류 해보자.
우리가 기본적으로 변수를 선언할 때, 변수는 4가지 정도의 정보를 가진다. 이를 다양한 언어에서의 변수 선언문을 통해 알아보자.
먼저 C를 보자.

int int_variable;
char char_variable;
char char_array_varialble[5];

C언어에서는 기본적으로 자료형 변수이름 배열의 길이(optional) 이라는 정보를 얻을 수 있으며, 이 정보를 토대로 메모리에서 영역을 확보 한다.
다음으로는 Java를 보자

int int_variable;
char char_variable;
char[] char_array_variable;
String string_variable = new String("Hello World");

Java에서도 똑같이 자료형 변수이름 배열의 길이(optional) 을 가지고 있다.
글이 길어질 것 같으니 여러 언어를 한번에 적어보겠다.

// Rust
let int_variable:i32 = 10;
let mut int_mut_variable:i32 = 10;
let int_eval_variable = 10;
// JavaScript
var int_variable = 10;
let int_let_variable = 10;

// TypeScript
var int_variable: number = 10;
let string_variable: string = "Hello World";
# Python
int_variable = 10
tuple_variable = (10, 20)

이렇게 다양한 언어에서는 각 언어에서 조금 씩 다른 ( 거의 C 베이스라 형태 자체는 동일 한 ) 모양의 변수 선언문을 가지게 된다.

전체적으로 한 번 알 수 있는 정보를 요약해보자.

  1. 자료형
  2. 변수이름
  3. 초기값
  4. 재할당가능 여부

이런 다양한 정보를 가지게 된다.

자료형

먼저 C, C++, C#, Java, VBA, Rust, TypeScript 에서는 변수의 자료형에 대해서 지정 해준다.
이들은 정적 타입이거나, 정적 타입 처럼 동작하는 언어이다.
재밌게도 2가지 언어는 자료 형이 가장 앞에 등장하지 않는데, 그것은 RustTypeScript이다.
그렇다면 RustTypeScript의 맨 앞 부분에는 어떤 정보가 등장할까?

특이점 of Rust

러스트의 맨 앞에는 let mut 키워드가 들어가는 것을 알 수 있고, let은 변수임을 의미하며, mut은 이 변수가 가변성을 가진다는 것을 의미한다. 즉, 이 변수는 값이 바뀔 수 있는 변수라는 의미이다. 이를 재할당이라고 한다. 그리고 자료형변수 이름 뒤에 선언 된 것을 확인 할 수 있는데, 없어도 된다. 처음 초기 값이 있는 경우에는 컴파일러에서 알아서 뒤의 식을 평가하여 자료형을 결정 한다.

특이점 of TypeScript

타입스크립트는 자바스크립트의 슈퍼셋이므로 기존적으로 동적 타입언어이나, 타입스크립트에서 개발 단계에서 결과의 타입계산하여, 에러를 발생시켜준다. 이를 타입 추론이라고 부른다. 가장 앞에 붙는 var let const의 경우는 자바스크립트와 동일 하니, 다른 주제에서 다루어 보겠다.

변수이름

변수이름은 당연하게도, 거의 모든 언어에서 등장한다 (심지어 어셈블리 마저도). 물론 컴퓨터에게는 필요하지만, 우리에겐 너무나도 필요하다...
왜냐면, 변수의 위치를 모두가 직접 선언하면서 그 위치를 참조하는 것은 너무나도 힘든일이고, 변수의 데이터와 자료형만으로, 이 변수가 어떤 역할을 하는지 알 수가 없어, 유지보수에서도 굉장히 불편할 것으로 보인다.

초기값

여기까지 적은 자료형, 변수이름, 초기값은 C 베이스 언어에서는 어떻게 보면 기본적인 내용으로 생각이 된다.
특히 Rust 와 또한 요즘 언어들 또 C++, C# 과 같은 언어에서도 최근에는 동적 자료형 키워드를 제공하며, 초기값에 따라 변수의 형태가 평가되는 언어가 종종 있기에, 기본적인 내용으로 더욱 생각 된다.'
초기값은 말그대로, 변수의 선언 이후 변수에 초기값을 할당하는 것인데 이를 초기화라고 부른다.

변수의 할당

재할당가능 여부에 앞서 할당이 무엇인가?에 대해서 알아보자.
할당은 쉽게 말하면 변수에 값을 넣는 과정이다. ( 변수에 이름 동작 과정과 밀접한 관련이 있으므로, 좀 더 자세히는 나중에 설명한다. )
다만 맨 처음 변수에 값을 할당하는 것초기화라고 부른다.

재할당가능 여부

재할당가능 여부는 변수가 초기화 된 이후 다시 값을 할당 할 수 있는가에 대한 여부를 말한다.
즉 처음에 값을 넣으면 다시 다른 값으로 변경이 가능한가에 대한 여부를 말한다. 변경가능한 변수를 mutable 변경 불가능한 것을 immutable 이라고 부른다.
c에서는 const키워드, java에서는 final키워드, javaScript에서는 const자료형이 immutable을 의미하며,
rust에서는 mut키워드를 사용하여 mutable한 변수를 선언한다.

변수의 자료형은 어떻게 동작할까?

그렇다면 변수의 자료형은 컴퓨터 내부에서는 어떻게 동작할까? (여기서 자료형은 정수형, 실수형, 문자형, 객체과 같은 정보만을 말한다)
실제로는 자료형을 통해 변수형태, 재할당여부, 재선언여부, 변수의 크기 등 다양한 정보를 알 수 있다.
먼저, 자료형은 하드웨어단계 에서는 데이터의 자료형을 알 수 없기때문에 소프트웨어 단계에만 영향을 미치는 정보이다.

당연하게도 메모리는 0과 1만을 저장 할 수 있기때문에, 자료형에 대해서 판단 할 수 없다.

이로 인해 프로그래밍 언어에 따라 2가지 Type Checking 을 하게 되는데, 이에 대한 설명으로는 다음 사이트에 잘 정리되어 있다.

  1. https://www.geeksforgeeks.org/type-checking-in-compiler-design/
  2. https://www.baeldung.com/cs/statically-vs-dynamically-typed-languages

쉽게 요약하면 아래와 같다

  1. Static Type Checking
  2. Dynamic Type Checking

Static Type CheckingCompile단계에서 타입을 확인하는 것으로, Java, C, Kotlin, Go, TypeScript 같은 언어에서 사용 된다.
이는 Run-Time단계에서의 버그 발생을 줄여주는 이 점을 가지고 있다.

Dynamic Type CheckingRun-Time단계에서 발생하는 타입 체킹으로, Python, JavaScript, PHP, Lua와 같은 언어에서 사용 된다.

변수의 이름은 어떻게 컴퓨터에 저장(기억) 될까?

그렇다면 마지막, 변수의 이름컴퓨터에서 어떻게 동작할까?
일단 변수의 이름은 변수 공간의 주소를 참조하는 정보라고 볼 수 있다. 위에서 적었다시피, 변수는 그저 공간이며, 이 공간을 가르키는 식별자이름이라고 한다.
그렇다면 이 식별자는 프로그램내에서 어딘가에 저장이 되는 것일까?
정답은 각각 다르다, 이것에 대해서 알아보자.

컴파일 언어에서 보는 변수 이름 동작 방식

컴파일 언어에서는 컴파일 단계에서 변수의 선언 위치크기, 범위를 모두 알 수 있다.
때문에 컴파일과정 중 변수명은 자동으로 메모리 주소로 치환된다. 이는 일종의 컴파일러 최적화이다.
이 의미는 실제로 변수가 어디에 저장되는 것은 아니고, 변수 이름메모리 주소로 치환되며, 성능이나 메모리 사용량에는 영향을 끼치지 않음을 의미한다.
좀 더 설명하자면, 컴파일 과정과, 프로그램이 실행 되면서 메모리의 text공간에 모든 코드를 read하면서 이미 모든 변수의 메모리 위치가 정해지므로, 이 과정에서 이름크게 의미를 가지지 않으며, 메모리 주소치환된다.

인터프린터 언어에서 보는 변수 이름 동작 방식

하지만 인터프린터에서는 다르다.
인터프린터는 코드를 런타임에 실행하기 때문에, 컴파일 과정이 없고, 이로 인해 알 수 있는 정보가 없다.
언어마다 약간의 차이가 있을 수 있겠지만, 자바스크립트에서는 한번 전체 파일을 읽어 호이스팅이라는 과정을 통해 메모리 주소를 할당한다.
다만 아래에도 적겠지만, 이 경우 실행 환경에서 메모리 주소를 기억하기 위해 변수 이름또한 기억한다.

바이트코드 언어에서 보는 변수 이름 동작 방식

그 외에도 C#, JAVA, JavaScript(NodeJS) 같은 언어들은 ByteCode 라는 것을 사용하기도 하는데, 이 경우 컴파일과정은 존재하나, 어셈블리어기계어로 번역 되는 것이 아닌 중간 단계ByteCode로 저장된다. 이는 각 언어의 특징 때문인데, C#.Net기반의 Framework에서 돌아가는 언어이고, JAVA또한 JVM위에서 돌아가는 언어이며, NodeJS의 경우는 V8위에서 돌아가는 언어이다.
이 경우 변수 이름자체는 바이트 코드에 기록이 된다. 왜냐하면 코드 실행 이전에는 아무리 바이트 코드컴파일을 하더라도 코드의 위치를 파악 할 수 없기 때문에, 변수의 이름이 바이트코드 파일에 그대로 적혀있게 된다. 이로 인해 재밌는 사실이 하나 있는데 다음 섹션을 보자.

(번외, 유머) 변수 이름의 길이는 인터프린터 성능에 유의미 하게 영향을 미치는가?

이섹션은 https://betterprogramming.pub/does-variable-name-length-affect-javascript-code-speed-bedc3cf592a8 이 글을 참조하여 쓴 섹션입니다.

ByteCode에서는 이전에 말했다시피, 바이트코드에 모든 변수이름이 적히게 된다 때문에 재밌는 현상이 하나 발생한다.
위 글을 요약하자면, 바이트코드가 실행 되는 Run-Time에서의 성능차이는 존재하지 않으나, 매우 극단적인 경우 변수 이름 길이로 인해 바이트 코드 파일의 길이가 길어지면서 실행 시간이 조금 달라질 수 있다! ( 하지만 길고 긴 커리어중에서도 만날 수 있는 정도가 아니다 > 매우 극단적.. )

자바스크립트에서의 변수 선언

자바스크립트의 변수선언문은 아래 형태를 따른다.

[변수속성] [변수이름] = [초기값]

여기서 변수속성은 임의로 이름을 정했다.. ( 정확한 이름을 아시는 분은 댓글 부탁드립니다. )

  • 변수속성
    • var
    • let
    • const

변수속성은 위 3가지가 있다.

이 3가지를 표로 비교해보자

변수속성재선언가능 여부재할당가능 여부
varOO
letXO
constXX

다음으로 변수의 타입은 크게 2개로 구분된다.

  1. 원시 형태 (primitive type 이라고도 한다.)
    • Number
    • String
    • Boolean
    • Null
    • Undefined
    • Symbol
  2. 참조 형태 (reference type 이라고도 한다.)
    • Array
    • Object

이 두 가지는 메모리 저장 영역에서 차이를 보인다.
아래 섹션을 보자.

자바스크립트의 CallStack과 MemoryHeap

자바스크립트 변수는 선언 시 기본적으로 CallStackMemoryHeap 영역에 저장 된다.
콜스택메모리힙자바스크립트 환경 내부에서 실행되는 것으로, Stack은 잘 알다시피, 지역변수 같은 정해진 값들이 저장되는 공간이며, Heap은 프로그래머가 동적으로 할당할 때 사용 되는 영역이다.
자바스크립트 또한 위 특징을 똑같이 따른다.

자바스크립트에서 .js 파일을 읽어 parsing하며, hoisting을 통해 먼저 변수 선언 자체를 끌어올린다. ( 이쯤되면 예상했겠지만, 선언만 할뿐 초기화는 하지 않는다. )
이때 변수들은 stack 영역에 할당된다.

그렇다면 이후에 위 섹션에서 적은 원시타입참조타입에서 변수 선언 동작이 약간의 차이가 발생하게 되는데, 자바스크립트의 ArrayObject는 제한 없이 할당이 가능하므로 그 크기를 예측할 수 없다. 이로 인해 차이가 발생하게 된다.

먼저 원시타입의 경우는 크기가 동적이지 않다. 때문에 Stack영역에 직접 선언이 가능하다.
하지만 참조타입의 경우는 크기가 동적이기 때문에, 동적 확장이 가능한 Heap영역이 데이터를 저장하는 영역이 된다.
그리고 변수자체는 Stack에 저장이 되며, 이 Stack공간에는 Heap영역의 주소를 참조하게 된다. ( 그래서 참조타입이다 )
이것을 다른언어와 비교하게 된다면 C의 Array(배열)은 크기가 정적이기 때문에 Stack에 저장이 될 수 있으나, malloc으로 동적할당을 하게 되면, Heap영역에 메모리가 할당 되는 것과 같은 원리로 볼 수 있다. C 또한 이 경우 보통 pointer라는 것을 이용하여 주소 값으로 값을 컨트롤 한다.

variable             callstack           memory heap
a -----------------> 1
b -----------------> &105 -------------> []
c -----------------> &165 -------------> {}

자바스크립트의 불변성

자바스크립트의 불변성은 리액트 불변성과 크게 관련있는 부분이라 정리한다.

자바스크립트에서의 원시 타입불변성을 가지게 되는데, 데이터 자체불변하다는 의미이다.
이로 인해, 자바스크립트에서 변수의 값을 변경하게 되면 재밌는 특징이 나타난다.

자바스크립트 변수 할당의 메모리 변화

바로 primitive type의 값은 immutable하기 때문에 변수 이름이 가르키는 변수변화한다.

let a = 10;
a = a + 1;

이라고 하였을때, number타입인 10불변함으로, a + 1새로운 변수에 저장되며, 식별자 a는 그 새로운 주소를 가르킨다.
이때 식별자a가 어떤 주소를 가르키고 있는지를 기억하기 위해 JS Engine변수명실행 환경 내부에서 따로 기억하고 있다.
이렇게 보면 JS Engine 이라는 프로그램 내에서 변수 이름은 또 다른 실행 환경의 변수의 주소를 기억하기 위한 또 하나의 정보라고 할 수 있다. ( C 에서는 컴파일 단계 이후, 이미 변수 이름무의미하다 )


또 다른 흥미로운 내용에 대해 공부한다면 다시 정리해보겠다!

Bye World!

profile
수상할 정도로 루피를 사랑하는 개발자

1개의 댓글

comment-user-thumbnail
2023년 5월 20일

알찬 내용 감사합니다!!

답글 달기