프론트엔드/JavaScript

[4장 변수] 모던 자바스크립트 Deep Dive

의현 2025. 2. 20. 17:35

 

변수란 무엇인가? 왜 필요한가?

  • 변수는 프로그래밍 언어에서 데이터를 관리하기 위한 핵심 개념이다. 
10 + 20
  • 컴퓨터는 사람을 모델로 디자인되었기 때문에 사람과 유사하게 동작한다. 사람은 위 식을 계산하려면 10, 20, +라는 기호의 의미를 알고 있어야 하며, 10 + 20이라는 식의 의미도 해석할 수 있어야 한다.
  • 컴퓨터, 좀 더 정확히 표현하면 자바스크립트를 해석하고 실행하는 자바스크립트 엔진도 사람과 유사하게 자바스크립트 코드를 실행한다.
  • 10 + 20을 계산하려면 먼저 10, 20, +라는 기호(literal 리터럴과 operator연산자)의 의미를 알고 있어야 하며, 10 + 20이라는 표현식(expression)의 의미도 해석(parsing 파싱)할 수 있어야 한다.
  • 컴퓨터는 CPU를 사용해 연산하고, 메모리를 사용해 데이터를 저장한다.
    • 메모리는 데이터를 저장할 수 있는 메모리 셀의 집합체이다. (셀 하나의 크기는 1바이트, 8bit)
    • 각 셀은 고유한 메모리 주소를 갖는다.
    • 또한, 모든 데이터를 2진수로 처리한다.
  • 즉, 10 + 20을 한 30이라는 결과는 특정 메모리에 저장이 되어있다.

  • 위 그림 0x0669F913에 저장된 30을 재사용하고 싶다면 위 메모리 공간에 직접 접근하는 것 외에는 방법이 없다.
  • 하지만 메모리 주소를 통해 값에 직접 접근하는 것은 치명적 오류를 발생시킬 수 있어 자바스크립트는 개발자의 직접적인 메모리 제어를 허용하지 않는다.
  • 변수(variable)는 하나의 값을 저장하기 위해 확보한 메모리 공간 자체 또는 그 메모리 공간을 식별하기 위해 붙인 이름을 말한다.
    • 간단히 말하면 변수는 프로그래밍 언어에서 값을 저장하고 참조하는 메커니즘으로, 값의 위치를 가리키는 상징적인 이름이다.
var result = 10 + 20;
  • 10 + 20의 결과가 0x0669F913 메모리 공간에 저장이 되고, 변수로 선언한 result는 0x0669F913 메모리 공간에 접근을 할 수 있다.

식별자

  • 변수 이름을 식별자(identifier)라고도 한다.
  • 식별자는 어떤 값을 구별해서 식별할 수 있는 고유한 이름을 말한다.
  • 위에서 언급된 예제에서 result는 값 30을 식별할 수 있었다. -> result는 0x0669F913라는 메모리 주소를 기억하고 있다.
    • 식별자는 값이 저장되어 있는 메모리 주소와 매핑 관계를 맺고, 이 매핑 정보도 메모리에 저장되어야 한다.
  • 이처럼 식별자는 값이 아니라 메모리 주소를 기억하고 있다.
    • 식별자로 값을 구별해서 식별한다는 것은 식별자가 기억하고 있는 메모리 주소를 통해 메모리 공간에 저장된 값에 접근할 수 있다는 의미이다.
  • 즉, 식별자는 메모리 주소에 붙인 이름이라고 할 수 있다.
  • 메모리 상에 존재하는 어떤 값을 식별할 수 있는 이름은 모두 식별자이다.

변수 선언

  • 변수 선언이란 변수를 생성하는 것을 말한다.
    • 값을 저장하기 위한 메모리 공간을 확보하고 변수 이름과 확보된 메모리 공간의 주소를 연결해서 값을 저장할 수 있게 준비하는 것
  • 변수를 선언할 때 var, let, const 키워드를 사용 
    • let, const는 ES6부터 도입
    • let, const는 var의 스코프 문제로인해 도입이 되었고 [15장 내용 정리에서 확인]
  • 자바스크립트 엔진은 2단계를 거쳐 변수를 선언
선언 단계 : 변수 이름을 등록해서 자바스크립트 엔진에 변수의 존재를 알린다.
초기화 단계 : 값을 저장하기 위한 메모리 공간을 확보하고 암묵적으로 undefined를 할당해 초기화한다.

변수 선언의 실행 시점과 변수 호이스팅

console.log(score); // undefined
var score; // 변수 선언문
  • 자바스크립트 코드는 인터프리터에 의해 한 줄씩 순차적으로 실행되므로 console.log(score);가 가장 먼저 실행됨
  • 아직 score의 변수 선언이 실행되지 않았으므로 ReferenceError가 발생할 것 처럼보인다.
    • 하지만 참조 에러가 아닌 undefined가 출력된다.
    • 그 이유는 변수 선언이 소스코드가 한 줄씩 순차적으로 실행되는 시점, 즉 런타임(runtime)이 아니라 그 이전 단계에서 먼저 실행되기 때문이다.
<자바스크립트 실행순서>
코드 실행 -> 소스코드 평가(변수 선언을 포함한 모든 선언문(변수 선언문, 함수 선언문 등)을 먼저 실행) -> 선언문을 제외한 나머지 소스코드를 한 줄씩 순차적으로 실행
  • 실행 순서를 보면 자바스크립트 엔진은 변수 선언이 어디에 있든 다른 코드보다 먼저 실행한다. -> 어디서든 참조할 수 있다.
  • 위 예시에서 score가 ReferenceError가 발생하지 않는 이유는 score라는 변수가 소스코드 평가 단계에서 이미 변수 선언이 실행되었다는 증거이다.
  • 이처럼 변수 선언문이 코드의 선두로 끌어 올려진 것처럼 동작하는 자바스크립트 고유의 특징을 변수 호이스팅(variable hoisting)이라 한다.

값의 할당

  • 변수에 값을 할당할 때는 할당 연산자 =를 사용한다.
    • 우변의 값을 좌변의 변수에 할당한다.
// ex1
var score; // 변수 선언
score = 80; // 값의 할당

// ex2
var score = 80; // 변수 선언과 값의 할당
  • ex1과 ex2는 동일하다.
    • 자바스크립트 엔진은 ex2처럼 하나의 문으로 작성해도 변수 선언과 값의 할당으로 나누어 각각 실행한다.
    • 주의할 점은 값의 할당은 평가 다음 단계인 소스코드가 한 줄씩 순차적으로 실행되는 런타임에 실행된다.
console.log(score); // undefined

var score; // 변수 선언
score = 80; // 값의 할당

console.log(score); // 80
  • 첫 번째 console.log(score);가 undefined인 이유는 평가 단계에서 score 변수가 undefined로 초기화가 되고 값의 할당이 아직 이루어지지 않음
  • 런타임 실행 순서를 보면 console.log(score) -> score = 80 -> console.log(score) 순으로 처음 score은 undefined이고 이후 80으로 초기화가 되면서 다음 score은 80이 나오는걸 확인할 수 있다.

  • 주의할 점은 score에 80을 할당할 때 해당 메모리 공간의 undefined를 80으로 다시 저장하는게 아니라 새로운 메모리 공간을 확보하고 그곳에 80을 저장하고 score가 바라보는 메모리 주소를 바꿔주는 방식으로 처리된다.

값의 재할당

  • 변수의 재할당이란 이미 값이 할당되어 있는 변수에 새로운 값을 또다시 할당하는 것을 말한다.
var score = 80; // 변수 선언과 값의 할당
score = 90; // 값의 재할당
  • var 키워드로 선언한 변수는 값을 재할당할 수 있다.

  • 위 코드를 그림으로 나타낸 것이다.
  • score변수 선언(undefined) -> 80 할당(엄밀히 따지면 재할당임) -> 90 재할당
  • 위 값의 할당에서 봤듯이 값의 할당은 해당 메모리 공간에 직접 값을 변경하는게 아니라 새로운 메모리 공간에 값을 할당하고 변수가 바라보는 메모리 주소를 바꾸는 것이다.
    • 위 그림에서 undefined, 80의 메모리 공간은 더 이상 필요가 없어지게 된다.
    • 이러한 불필요한 값들은 가비지 콜렉터에 의해 메모리에서 자동 해제된다. (단, 언제 해제될지는 모름)
      • 가바지 콜렉터는 메모리 누수를 방지하기 위해 불필요한 메모리 공간을 해제하는 기능을 담당한다.

식별자 네이밍 규칙

  • 식별자는 어떤 값을 구별해서 식별해낼 수 있는 고유한 이름을 말한다.
식별자 네이밍 규칙
> 식별자는 특수문자를 제외한 문자, 숫자, 언더스코어(_), 달러 기호($)를 포함할 수 있다.
> 단, 식별자는 특수문자를 제외한 문자, 언더스코어(_), 달러 기호($)로 시작해야 한다. 숫자로 시작하는 것은 허용하지 않는다.
> 예약어는 식별자로 사용할 수 없다.
  • 네이밍 컨벤션(naming convention) : 하나 이상의 영어 단어로 구성된 식별자를 만들 때 가독성 좋게 단어를 한눈에 구분하기 위해 규정한 명명 규칙
// 카멜 케이스 (camelCase)
var firstName;

// 스네이크 케이스 (snake_case)
var first_name;

// 파스칼 케이스 (PascalCase)
var FirstName;

// 헝가리언 케이스 (typeHungarianCase)
var strFirstName; // type + identifier
var $elem = document.getElementById('myId'); // DOM 노드
var observable$ = fromEvent(document, 'click'); // RxJS 옵저버블