[JS] var, let, const의 차이점


 

이번에 웹 공부의 필요성을 느껴서 이전에 대강 대강 이해하고 넘어갔던 JS를 심화 학습중입니다. 기존 응용 프로그램만 개발하면서 언어 자체가 '어렵다' 정도는 있어도 (C++ 이라던가...) 난해하다는 느낌은 없었는데 JS의 경우 제가 기존에 프로그래밍 하던 것과 다른 난해한 느낌을 받았습니다.

 

특히 자바스크립트의 과도한 유연성이 저에겐 혼돈의 카오스 그 자체였습니다...

'JavaScript is Weird' 라는 말이 개발자들 사이에서 도는게 그 증거겠죠.

 

그 중 첫번째가 바로 JS에서 변수 선언 방식인 var, let, const의 차이입니다. 오늘은 이 3가지의 차이점에 대해 좀 자세히 알아보겠습니다.

 

*본 글에서 자바스크립트라는 단어는 전부 JS라는 단어로 축약해서 사용하겠습니다.

 

var vs let vs const

JS는 느슨한 타입의 동적 언어기 때문에 변수를 선언할 때 파이썬 처럼 자료형을 따로 명시해주지 않아도 됩니다. JS는 var, let, const 라는 3가지 키워드로 변수를 선언할 수 있습니다. 

 

원래 ES6 전까지는 var 키워드가 자바스크립트 변수를 선언 할 수 있는 유일한 키워드 였으나, ES6 이후로 let, const 가 새로 도입되었습니다.

 

var와 let은 크게 다르지 않아서 둘을 바꿔서 사용해도 큰 문제가 없지만 어떤 상황에서는 var가 상당히 이상해보입니다. 

자바스크립트의 ES란 ECMAScript의 약자이며, 자바스크립트 표준, 규격을 나타내는 용어입니다.
뒤의 숫자는 버전을 뜻하고 ES5는 2009년 ES6은 2015년에 출시되었습니다.

출처 - https://hanamon.kr/javascript-es6-%EB%AC%B8%EB%B2%95/

 

우선 기존에 사용하던 var을 보겠습니다.

 

var existed_num = 10;

/* 

Probably a lot of code...

*/

var existed_num = 20;

본 코드를 실행하면 문제 없이 실행이 됩니다.

보시면 같은 이름의 변수를 var로 2번 선언하였는데도 문제가 발생하고 있지 않습니다.

대부분의 프로그래밍 언어들은 이런 중복 선언을 허용하지 않습니다.

 

(정확히는 정의라고 해야겠지만 선언이라 표현해도 큰 문제는 없으니 본 글에선 선언이라고 하겠습니다)

 

그러나 var로 선언하면 중복 선언이 됩니다. 팀 단위 프로젝트를 진행할 때 중간에 주석을 아주 많은 양의 이미 작성된 코드라고 생각해봅시다. 막 개발을 시작한 프로그래머는 위에서 같은 이름의 변수가 이미 선언되었다는 걸 알지 못합니다, 중간에 코드가 너무 쌓여있기 때문이죠. 그래서 아무 생각없이 var로 선언을 해버렸고 이름이 겹쳤을 때 중복 선언이 되고 그냥 덮어쓰기가 되어서 기존에 선언해둔 변수의 내용은 홀라당 사라져버립니다.

 

let existed_num = 10;

/* 

Probably a lot of code...

*/

let existed_num = 20; //Error!!

let으로 선언하면 이런 문제를 해결 할 수 있습니다. C, C++ 와 같이 일반적인 프로그래밍 언어들의 변수 선언처럼 중복 선언시 오류가 발생하면서 실행이 제대로 되지 않습니다.

 

let existed_num = 10;
existed_num = 20; //OK

이미 만들어졌으면 재할당은 가능해야겠죠. let은 중복 선언이 금지될 뿐 당연히 재할당은 가능합니다.

 

const pi = 3.141592;
pi = 3.1415; //Error

const의 경우 일반적으로 아는 상수 선언을 생각해보시면 됩니다. 당연하지만 중복 선언은 불가능하고 재할당도 불가합니다. 상수기 때문에 처음 초기화 이후 값을 바꾸는거 자체가 말이 안되겠죠?

 

호이스팅

var로 선언하면 또 다른 문제가 있습니다. 아래 예제를 볼까요?

 

console.log(test);

var test = 10;

>>> undefined

var로 test라는 변수를 선언하기 전에 먼저 console.log로 test의 값을 찍어봤습니다. 얄짤 없이 오류가 찍힐것이라는 기대와 다르게 undefined 라는 다소 당황스러운 값이 나옵니다.

 

JS에서 undefined란 변수를 선언하고 값은 할당하지 않았다는 뜻입니다. 그러니 변수는 이미 존재하는데 값이 할당 되지 않았다는 뜻이죠. 예를 들어서 let age; 처럼 값은 할당하지 않고 console.log(age) 를 찍어보면 undefined 가 나옵니다.

 

분명 test 이전에 console.log 를 실행했는데 어째서 test가 이미 만들어져있는 상태일까요?

JS에서는 호이스팅 이란 것이 일어나서 test라는 변수를 최상위로 끌어올립니다.

 

즉 아래처럼 동작하는 것 입니다.

 

var test;

console.log(test);

test = 10;

>>> undefined

 

실제로 코드가 실제로 이동해서 올라간 건 아닙니다만 최상위로 끌어올려진것 처럼 동작하는 호이스팅이 일어난 것입니다. 호이스팅 시 선언은 호이스팅 되지만 할당은 호이스팅 되지 않아서 undefined 가 출력된 것이죠.

test라는 변수는 끌어올려지고 10이라는 값은 그 자리에 있다고 생각하시면 됩니다.

 

할당은 3번째 라인에서 처리하기 때문에 (test = 10) 이후에 console.log로 값을 다시 출력해보면 그때는 10이 잘 출력되겠죠.

 

console.log(test);
var test = 10;
test = 10;

>>> undefined
>>> 10

생각한대로 값이 잘 나와주었습니다. 근데 아무리 생각해도 코드 상으로 봤을때 변수가 선언도 되지 않았는데 이미 호이스팅 이란것에 의해 만들어진 것 처럼 동작하다니 참 난해하죠?

이제 var을 let으로 바꿔봅시다.

 

console.log(test); //Error
let test = 10;
test = 10;


>>> ReferenceError: Cannot access 'test' before initialization

이제 우리가 생각한 대로 test가 만들어지기 전에 test를 접근해보려 하니 오류가 발생합니다! 그럼 let은 호이스팅이 되지 않는 것이군요! 라고 생각할 수 있지만 let도 호이스팅 됩니다. (심지어 const 역시 호이스팅 됩니다.)

 

결론적으론 var, let, const 모두 호이스팅 됩니다.

 

호이스팅은 스코프 내부 어디서든 변수 선언은 최상위에 선언된 것처럼 행동한다는 뜻입니다. let은 어째서 var처럼 작동하지 않고 오류를 발생시키는 걸까요? 이에 대해 이해하려면 TDZ(Temporal Dead Zone) 에 대해 알아야 합니다. 이에 대해선 나중에 포스팅 하기로 하고 우선 let과 const는 TDZ에 영향을 받습니다.

 

TDZ를 간단하게 이해해보면, let과 const는 선언하기 전엔 스코프의 시작에서 변수의 선언까지 TDZ라는 일시적 사각지대에 빠져서 사용을 할 수 없게 됩니다.

 

호이스팅은 일반적으로 스코프 단위로 일어나는데 (중괄호 범위) TDZ역시 그 스코프 안에서 잡히기 때문에 let과 const는 선언 전엔 사용할 수 없게 됩니다.

 

제대로 글에 담아내지 못해서 아쉽지만, 쉽게 생각해서 let과 const는 일반적으로 C/C++ 이나 대부분의 프로그래밍 언어에서 보았던 그런 변수 선언들, 우리가 예측할 수 있고 기대할 수 있는 행동으로 동작한다는 것입니다. let, const 는 호이스팅이 발생하지 않는 것처럼 동작합니다. (물론 실제로 JS는 var let const 모두 호이스팅을 일으킵니다.)

 

결론

의도치 않은 프로그램의 변동성을 줄이기 위해서 우선은 const로 변수를 선언하고, 그 값이 바뀌어야 한다면 let으로 선언합니다. var은 사용하지 않습니다.

 

 

 

 

<참고>

https://www.youtube.com/watch?v=4_WLS9Lj6n4&t=2s 

 

COMMENT WRITE