목표
4장 변수
4-1. 변수(Variable)란 무엇인가?
- 컴퓨터는 사람을 모델로 디자인 되었기 때문에 사람과 유사하게 동작한다고 한다.
10 + 20
4-1-1. 사람의 연산 과정
- 먼저, 사람이 위 식을 계산하려면 10과 20이라는 숫자 그리고 +라는 기호의 의미를 알고 있어야 한다.
- 또한, 10 + 20 이라는 식의 의미를 해석할 줄 알아야 한다.
- 식의 의미를 해석하면 덧셈을 하기 위해 10과 20을 두뇌에 기억하며 결과값인 30 또한 두뇌에 기억한다.
4-1-2. 컴퓨터의 연산 과정
- 컴퓨터, 구체적으로 자바스크립트를 해석하고 실행하는 자바스크립트 엔진도 사람과 비슷하다.
- 먼저 연산자(+), 피연산자(10, 20)의 의미를 알고 있어야 하며 10 + 20 이라는 표현식의 의미도 알고 있어야 한다.
- 사람과의 가장 큰 차이점은 사람은 계산과 기억을 한 곳(두뇌)에서 하지만 컴퓨터는 연산을 담당하는 부분(CPU)과 기억을 담당하는 부분(메모리)이 나뉘어져 있다.
메모리(memory)란
-
메모리는 데이터를 저장할 수 있는 메모리 셀(memory cell)의 집합체이다.
-
메모리 셀의 크기는 1byte(8bit)이다. 즉, 컴퓨터는 1byte 단위로 데이터를 저장(Write)하고 읽어(Read) 들인다.
-
각 셀은 고유의 메모리 공간의 위치를 나타내는 메모리 주소(memory address)를 갖는다.
-
메모리의 저장되는 모든 값은 이진수로 저장된다.
4-1-3. 변수는 왜 필요한가?
- 30이라는 연산 결과를 한 번만 사용하면 문제가 없겠지만 연산 결과를 재사용한다고 했을 때는 문제가 발생한다.
- 물론, 메모리 주소를 통해 연산 결과 30이 저장된 메모리 공간에 접근하는 방법이 있겠지만 이는 치명적인 오류를 발생시킬 확률이 높은 아주 위험한 행동이다.
- 예를 들어, 운영체제가 실제로 사용하고 있는 값을 저장된 메모리 공간에 접근해 값을 변경하면 시스템이 멈추게 되는 치명적인 오류가 발생한다. 또한, 동일한 컴퓨터에서 동일한 코드를 실행해도 값이 저장된 메모리 주소가 똑같다고 보장할 수 없다.
- 이처럼 메모리 주소를 통해 값에 직접 접근하는 것은 올바른 방법이 아니다. 대부분의 언어가 그렇겠지만, 자바스크립트에서는 개발자의 직접적인 메모리 제어를 허용하지 않는다.
- 따라서, 프로그래밍 언어는 기억하고 싶은 값을 메모리에 저장하고 저장된 값을 읽어 들여 재사용하기 위해 변수(Variable)라는 매커니즘을 제공한다.
4-1-4. 그래서 변수가 뭔데요?
var result = 10 + 20
- 한마디로 연산 결과를 여러 곳에서 사용하고 싶을 때 사용하는 이름이다.
- 정확히 말하자면, 하나의 값을 저장하기 위해 확보한 메모리 공간 자체 그리고 그 메모리 공간을 식별하기 위해 붙인 이름이다.
- 변수를 통해 값을 읽어 들이려고 요청을 하면 자바스크립트 엔진은 변수명과 매핑된 메모리 주소를 통해 메모리 공간에 접근해서 저장된 값을 반환한다.
4-2. 식별자(Identifier)
- 식별자는 어떤 값을 구별해서 식별할 수 있는 고유한 이름을 말한다. 즉, 변수에서는 변수명이 식별자에 해당된다.
🤔 의문
-
값은 임의의 메모리 주소에 해당하는 메모리 공간에 저장되어 있다. 그렇다면 식별자는 무엇을 저장해야 메모리 공간에 있는 값에 접근 할 수 있을까?
답은 질문에 나와 있듯이 메모리 주소를 저장해야 한다. 식별자가 기억하고 있는 메모리 주소를 통해 메모리 공간에 저장된 값에 접근할 수 있다.
- 참고로 식별자는 변수 뿐만 아니라 함수, 클래스 등 메모리 상에 존재하는 어떤 값을 식별할 수 있는 이름은 모두 식별자라고 부른다.
4-3. 변수 선언(Declaration)
- 변수, 함수, 클래스 등의 식별자는 선언을 함으로써 자바스크립트 엔진에 존재를 알린다.
- 즉, "변수를 선언한다." 의 의미는 값을 저장하기 위한 메모리 공간을 확보하고 변수명과 확보된 메모리 주소의 공간의 주소를 연결해서 값을 저장할 수 있게 준비하는 것이다.
- 변수 선언에 의해 확보된 메모리 공간은 확보가 해제되기 전까지 누구도 학보된 메모리 공간을 사용할 수 없도록 보호되므로 안전하게 사용할 수 있다.
4-3-1. 자바스크립트의 변수 선언
- 자바스크립트에서 변수를 선언할 때는 var, let, const 키워드를 사용한다.
- ES6 문법이 도입되기 전까지 변수를 유일하기 선언할 수 있었던 키워드는 var였다.
- 하지만, var 키워드를 사용했을 때 여러 단점들이 존재했는데 가장 대표적인 것이 함수 레벨 스코프를 지원한다는 것이다. 이는 의도치 않게 전역 변수가 선언되어 심각한 문제가 발생할 수 있다.
(자세한 내용은 스코프에서 확인할 수 있다.)
- 먼저, 다음과 같이 var 키워드를 사용해 변수를 선언해보면 다음과 같다.
var scope;
- 변수를 선언했으므로 자바스크립트 엔진은 변수명을 등록하고 저장할 메모리 공간을 확보한다.
🤔 의문
-
그렇다면 코드에서는 변수에 값을 할당하지 않았으므로 메모리 공간에는 빈 값으로 존재하는 것일까?
빈 값으로 존재하는 것이 일반적이다. 하지만, 자바스크립트 엔진에 의해 메모리 공간에는 undefined 라는 값이 암묵적으로 할당되어 초기화된다.
undefined
- 자바스크립트에서 제공하는 원시 타입의 값이다.
- 자세한 내용은 데이터 타입에서 확인할 수 있다.
-
결국, 자바스크립트 엔진은 변수 선언을 다음과 같이 2단계에 걸쳐 수행한다.
(1) 선언 단계
변수명을 등록해 자바스크립트 엔진에 변수의 존재를 알린다.
(2) 초기화 단계
메모리 공간을 확보하고 암묵적으로 undefined를 할당해 초기화한다.
4-3-2. 초기화를 거치는 이유
🤔 의문
-
자바스크립트 엔진이 암묵적으로 초기화를 진행하는 이유는 무엇일까?
결론부터 말하자면 쓰레기 값(garbage value)를 없애기 위해서다.
- 초기화 단계를 거치지 않는다면 확보된 메모리 공간에 다른 애플리케이션이 사용했던 값이 남아 있을 수 있다. 이러한 값을 쓰레기 값이라고 한다.
- 이처럼 var 키워드는 암묵적으로 초기화를 수행하므로 원하지 않는 값을 참조하는 행위를 방지할 수 있다.
4-4. 호이스팅(hoisting)
console.log(scope);
var scope;
- 위 코드를 실행하면 어떤 결과가 발생할까?
- 자바스크립트 코드는 인터프리터에 의해 한 줄씩 순차적으로 실행되므로 console.log(scope);가 가장 먼저 실행된다.
- 하지만, 아직 scope 변수의 선언이 실행되지 않았으므로 참조 에러(ReferenceError)가 발생할 것으로 예상된다.
참조 에러(ReferenceError)
-
값을 참조하려 했지만 자바스크립트 엔진이 등록된 식별자를 찾을 수 없을 때 발생하는 에러이다.
- 하지만, 예상과는 다르게 참조 에러가 발생하지 않고 undefined가 출력된다.
- 그 이유는 변수 선언이 한 줄씩 순차적으로 실행되는 시점, 즉 런타임(runtime)이 아니라 그 전 단계에서 실행되기 때문이다.
🤔 의문
자바스크립트 엔진은 소스코드를 한 줄씩 순차적으로 실행되기 전 평가 과정을 거치면서 실행 준비를 하기 때문이다.
- 평가 과정에서 자바스크립트 엔진은 변수 선언을 포함한 모든 선언문을 소스코드에서 찾아내 먼저 실행한다.
- 그리고 이 평가 과정이 끝나야 비로소 변수 선언을 포함한 모든 선언문을 제외하고 한 줄씩 실행한다.
- 즉, 자바스크립트 엔진은 변수 선언이 소스코드 어디에 있든 상관 없이 어디서든지 변수를 참조할 수 있다.
- 이처럼, 변수 선언문이 코드의 선두로 끌어 올린 것처럼 동작하는 자바스크립트 고유의 특징을 호이스팅(hoisting)이라고 한다.
- 참고로 호이스팅은 변수뿐만 아니라 함수, 클래스 모두 적용된다.(단, 전역 변수에 한정된다.)
4-5. 값의 할당
var scope;
scope = 10;
- 변수에 값을 할당할 때는 할당 연산자(=)를 사용한다. 좌변에는 변수명을 우변에는 값을 할당한다.
- 변수 선언과 할당을 밑의 코드와 같이 하나로 표현할 수 있다. 위 아래 두 코드는 동일하게 동작한다.
var scope = 10;
- 하지만, 변수 선언과 값의 할당의 실행 시점이 다르다는 것이 중요하다.
- 변수 선언은 이전에 언급했듯이 호이스팅으로 인해 런타임(순차적으로 실행되는) 이전에 먼저 시작하지만 값의 할당은 소스코드가 순차적으로 시작하는 런타임에 실행된다.
console.log(scope);
var scope;
scope = 10;
console.log(scope);
- scope 변수에 값을 할당하기 전에 이미 변수 선언이 끝났고 메모리 공간을 확보해 undefined 로 초기화 되어 있다.
- 그리고 scope 변수에 값을 할당하면 undefined에서 새롭게 할당한 숫자 80으로 재할당된다.
🤔 의문
-
그렇다면 undefined 가 선언된 메모리 주소와 80으로 재할당 된 메모리 주소가 같을까?
그렇지 않다. undefined 가 저장된 메모리 주소에 해당하는 메모리 공간에 값을 지워서 다시 사용하는 것이 아니라 다른 메모리 주소에 새로운 공간을 확보하고 다른 그 곳에 값 80을 저장한다.
4-5-1. 가비지 컬렉터(garbage collector)
- 위에서 언급했듯이 변수에 값을 할당할 때 다른 메모리 주소에 공간을 확보한다. 다시 말해 기존에 할당되었던 메모리 주소의 공간은 어떤 식별자와도 연결되어 있지 않다.
- 그렇다면 undefined 가 저장되었던 메모리 주소에 해당하는 공간은 어떻게 될까?
- 이러한 불필요한 값들은 가비지 컬렉터에 의해 메모리에서 자동 해제 된다.
가비지 컬렉터(garbage collector)
-
가비지 컬렉터는 애플리케이션이 할당한 메모리 공간을 주기적으로 검사하여 사용하지 않은 메모리 즉, 어떤 식별자도 참조하지 않는 메모리 공간을 해제하는 역할을 한다.
-
자바스크립트는 가비지 컬렉터를 내장하고 있기 때문에 메모리 누수를 방지할 수 있다.
※ 값의 재할당(4-6), 식별자 네이밍 규칙(4-7)는 생략