변수

개발 log·2021년 9월 5일
0

JS 지식

목록 보기
3/36
post-thumbnail

변수란 무엇이고 왜 필요할까?

App은 데이터를 다룹니다, 아무리 복잡한 App이라도 데이터를 입력받아 처리하고 그 결과를 출력하는 것이 전부입니다.

변수는 프로그래밍 언어에서 데이터를 관리하기 위한 핵심 개념입니다.

변수란 무엇이고 왜 필요한지 알아보겠습니다.

아래와 같은 JS코드를 실행하면 컴퓨터에서는 어떤 일이 일어날까요?

10 + 20

컴퓨터는 사람을 모델로 디자인되었기 때문에 사람과 유사하게 동작합니다.

사람은 어떻게 계산할까요?

사람이 위 식을 계산하려면 10, 20, +라는 기호의 의미를 알고 있어야합니다.
10 + 20 이라는 식의 의미도 해석할 수 있어야합니다.
10과 20을 두뇌에 기억하고 +기호의 결과인 30도 두뇌에 기억합니다.

컴퓨터(JS엔진)도 사람과 유사하게 위의 코드를 실행합니다.
JS엔진이 위 코드를 계산(평가)하려면 먼저 10, 20, + 라는 기호(리터럴과 연산자)의 의미를 알고 있어야하며, 10 + 20 이라는 식(표현식)의 이미도 해석(파싱)할 수 있어야합니다.
JS엔진이 10 + 20이라는 식의 의미를 해석하면 +연산을 수행하기 위해 +연산자의 좌변과 우변 즉, 피연산자를 기억합니다.
사람은 계산과 기억을 모두 두뇌에서 하지만, 컴퓨터는 연산과 기억을 수행하는 부품이 나눠져 있습니다.
CPU를 사용해 연산하고 메모리를 사용해 데이터를 기억합니다.
메모리는 데이터를 저장할 수 있는 메모리 셀의 집합체입니다.
메모리셀 하나의 크기는 1바이트(8비트)이며 컴퓨터는 메모리 셀의 크기 즉, 1바이트 단위로 데이터를 저장하거나 읽어들입니다.

각 셀은 고유의 메모리주소를 갖습니다.
해당 메모리주소는 메모리 공간이 위치를 나타내며, 0부터 시작해서 메모리의 크기만큼 정수로 표현됩니다.
컴퓨터는 모든 데이터를 2진수로 처리하기 때문에 메모리에 저장되는 데이터는 데이터의 종류와 상관없이 모두 2진수로 저장됩니다.

성공적으로 연산을 끝낸 후 연산 결과 역시 메모리에 저장되었지만 문제가 있습니다.
CPU가 연산해서 만들어낸 숫자값 30을 재사용할 수 없다는 것입니다.
만약 연산결과 30을 재사용하고 싶다면 메모리주소를 통해 연산 결과 30이 저장된 메모리 공간에 직접 접근하는 것 외에는 방법이 없습니다.
하지만 메모리주소를 통해 값에 직접 접근하는 것은 치명적오류를 발생시킬 가능성이 높은 매우 위험한 일입니다.
만약 실수로 운영체제가 사용하고 있는 값을 변경하면 시스템을 멈추게 하는 치명적인 오류가 발생할수도 있습니다.

만약 JS가 직접적인 메모리 제어를 허용하더라도 문제가 있습니다.
값이 저장될 메모리 주소는 코드가 실행될 때 메모리의 상황에 따라 임의로 결정됩니다.
따라서 동일한 컴퓨터에서 동일한 코드를 실행해도 코드가 실행될 때마다 값이 저장될 메모리 주소는 변경됩니다.
때문에 코드가 실행되기 이전에는 값이 저장된 메모리 주소를 알 방법이 없으며 알려주지도 않습니다, 그러니 메모리 주소값을 통해 값에 직접 접근하려는 시도는 올바른 방법이 아닙니다.

이 때 필요한 것이 변수입니다.
변수는 값의 위치(주소)를 기억하는 저장소입니다.
값의 위치란 값이 위치하고 있는 메모리 상의 주소(address)를 의미합니다.
즉, 변수란 값이 위치하고 있는 메모리 주소(Memory address)에 접근하기 위해 사람이 이해할 수 있는 언어로 명명한 식별자(identifier)입니다.

간단히 말하면 값의 위치를 가리키는 상징적인 이름입니다.




식별자

변수이름을 식별자라고도 합니다.

식별자는 어떤 값을 구별해서 식별할 수 있는 고유한 이름을 말합니다.

   result     =>    0x0669F913   | 30 |
변수이름(식별자)       메모리 주소    메모리

값은 메모리 공간에 저장되어 있고 식별자는 메모리 공간에 저장되어 있는 특정 값을 구별해서 식별해 낼 수 있어야합니다, 이 때문에 식별자는 어떤 값이 저장되어 있는 메모리주소를 기억해야 합니다.

즉, 식별자는 값이 저장되어 있는 메모리 주소와 매핑 관계를 맺으며, 해당 매핑 정보도 메모리에 저장되어야 합니다.

이처럼 식별자는 값이 아니라 메모리 주소를 기억하고 있습니다.

즉, 식별자 === 메모리 주소에 붙인 이름




변수 선언

변수 선언 === 변수 생성

좀 더 자세히 말하자면 값을 저장하기 위한 메모리 공간을 확보하고 변수 이름과 확보된 메모리 공간의 주소를 연결해서 값을 저장할 수 있게 준비하는 것입니다.

변수선언에 의해 확보된 메모리 공간은 확보가 해제되기 전까지는 아무도 확보된 메모리 공간을 사용할 수 없도록 보호되므로 안전하게 사용할 수 있습니다.

var 키워드의 대표적인 단점
블록레벨 스코프가 아닌 함수레벨 스코프를 지원하는 것
이 때문에 의도치 않게 전역 변수가 선언되어 심각한 부작용이 발생하기도 함

변수를 선언한 후 변수에 값을 할당하지 않으면 변수 선언에 의해 확보된 메모리 공간은 비어있을 것으로 생각할 수 있으나 확보된 메모리 공간에는 JS엔진에 의해 undefined라는 값이 암묵적으로 할당되어 초기화됩니다. (JS의 독특한 특징)

JS 엔진은 변수선언을 아래와 같은 2단계에 거쳐 수행합니다.

1. 선언단계 : 변수 이름을 등록해서 JS엔진에 변수의 존재를 알림
2. 초기화단계 : 값을 저장하기 위한 메모리 공간을 확보하고 암묵적으로 undefined를 할당하여 초기화

초기화의 중요성

만약 초기화 단계를 거치지 않으면 확보된 메모리 공간에는 이전에 다른 애플리케이션이 사용했던 값이 남아있을 수 있습니다.

이러한 값을 쓰레기값(garbage value)이라고 합니다.

따라서 메모리 공간을 확보한 후 값을 할당하지 않은 상태에서 곧바로 변수 값을 참조하면 쓰레기 값이 나올 수 있습니다.

JS의 var 키워드는 암묵적으로 초기화를 수행하기때문에 이런 위험으로부터는 안전합니다.




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

console.log(score); //undefined

var score; // 변수 선언문

JS코드는 인터프리터에 의해 한 줄씩 순차적으로 실행되기 때문에 console.log(score);가 먼저 실행되고 순차적으로 다음 줄에 있는 코드르 실행합니다.

따라서 위의 코드가 실행되는 시점에는 아직 score변수의 선언이 실행되지 않았으므로 참조 에러가 발생할 것처럼 보이지만 undefined가 출력됩니다.

그 이유는 변수 선언이 소스코드가 한 줄씩 순차적으로 실행되는 시점(런타임)이 아니라 그 이전단계에서 먼저 실행되기 때문입니다.

JS엔진은 소스코드를 순차적으로 실행하기에 앞서 소스코드의 평가과정을 거치면서 소스코드를 실행하기 위한 준비를 합니다.

이 단계에서 JS엔진은 변수 선언을 포함한 모든 선언문(변수, 함수 선언문, 식별자)을 소스코드에서 찾아내 먼저 실행합니다, 그 후 소스코드를 한 줄씩 순차적으로 실행합니다.

이처럼 변수 선언문이 코드의 선두로 끌어 올려진 것처럼 동작하는 JS 고유의 특징호이스팅이라고 합니다.




값의 할당

주의할 점

변수 선언과 값의 할당의 실행 시점이 다름

변수 선언은 소스코드가 순차적으로 실행되는 시점인 런타임 이전에 먼저 실행

하지만 값의 할당은 소스코드가 순차적으로 실행되는 시점인 런타임에 실행

console.log(score); // undefined

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

console.log(score); // 80

4-10 문제

console.log(score); // undefined

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

console.log(score); // ?? -> 80

이유
var score를 런타임 이전에 실행시켜 undefined로 초기화시키고
console.log(score) 실행 -> undefined
score에 값 할당 score = 80;
console.log(score); 실행 -> 80




값의 재할당

재할당 : 이미 값이 할당되어 있는 변수에 새로운 값을 다시 할당하는 것

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

재할당은 변수에 저장된 값을 다른 값으로 변경함 때문에 변수라고 하는 것
만약 값을 재할당할 수 없어서 변수에 저장된 값을 변경할 수 없다면 변수가 아니라 상수

변수 : 저장된 값을 재할당 가능한 것
상수 : 한번 값이 정해지면 변하지 않는 것


재할당 특이점

변수에 값을 재할당하면 처음 값을 할당했을 때와 마찬가지로 이전 값 80이 저장되어 있던 메모리 공간을 지우고 그 공간에 재할당 값 90을 새롭게 저장하는 것이 아닌 새로운 메모리 공간을 확보하고 해당 메모리 공간에 숫자 값 90을 저장합니다.

식별자를 잃은 undefined와 80은 불필요한 값들이므로 가비지 콜렉터에 의해 메모리에서 자동 해제되지만 언제 해제될지는 예측할 수 없습니다.

가비지 콜렉터

가비지 콜렉터는 애플리케이션이 할당한 메모리 공간을 주기적으로 검사하여 더 이상 사용되지 않는 메모리를 해제하는 기능을 말합니다.

더 이상 사용되지 않는 메모리란 식별자를 잃은 메모리 공간을 의합니다.

JS는 가비지 콜렉터를 내장하고 있는 매니지드 언어로써 가비지 콜렉터를 통해 메모리 누수를 방지합니다.

언매니지드 언어와 매니지드 언어

프로그래밍 언어는 메모리 관리방식에 따라 언매니지드 언어와 매니지드 언어로 분류할 수 있습니다.

C 언어 같은 언매니지드 언어는 개발자가 명시적으로 메모리를 할당하고 해제하기 위해 malloc()free()같은 저수준 메모리 제어 기능을 제공합니다.

언매니지드 언어는 메모리 제어를 개발자가 주도할 수 있으므로 개발자의 역량에 따라 최적의 성능을 확보할 수 있지만 그 반대의 경우 치명적 오류를 생산할 가능성도 있습니다.

JS같은 매니지드 언어는 메모리의 할당 및 해제를 위한 메모리 관리 기능을 언어 차원에서 담당하고 개발자의 직접적인 메모리 제어를 허용하지 않습니다.

즉, 개발자가 명시적으로 메모리를 할당하고 해제할 수 없다는 것입니다.

더 이상 사용하지 않는 메모리의 해제는 가비지 콜렉터가 수행하며, 이 또한 개발자가 관여할 수 없습니다.

매니지드 언어는 개발자의 역량에 의존하는 부분이 상대적으로 작아져 어느 정도 일정한 생산성을 확보할 수 있다는 장점이 있지만 성능면에서 어느 정도의 손실은 감수할 수밖에 없습니다.




식별자 네이밍 규칙

  1. 식별자는 특수문자를 제외한 문자, 숫자, 언더스코어(_), 달러기호($)를 포함할 수 있습니다.
  2. 단 식별자는 특수문자를 제외한 문자, _, $로 시작해야 합니다, 숫자로 시작하는 것은 허용하지 않습니다.
  3. 예약어는 식별자로 사용할 수 없습니다.
    • 식별자로 사용가능하나 strict mode에서는 사용 불가

대소문자를 구별함
변수이름은 변수의 존재 목적을 쉽게 이해할 수 있도록 의미를 명확히 표현해야함

  • break
  • case
  • catch
  • continue
  • debugger*
  • default
  • delete
  • do
  • else
  • finally
  • for
  • function
  • if
  • in
  • instanceof
  • new
  • return
  • switch
  • this
  • throw
  • try
  • typeof
  • var
  • void
  • while
  • with

출처: https://blog.sonim1.com/118 [Kendrick's Blog]

profile
프론트엔드 개발자

0개의 댓글