혹시 자바스크립트에서 var 사용을 지양하라는 말을 들어보셨나요? var는 한때 자바스크립트에서 변수를 선언할 수 있는 유일한 키워드였습니다. 하지만 ES6부터 모던 자바스크립트를 사용하는 개발자들은 더 이상 var를 사용하지 않고 있습니다. ES6부터 포함된 const와 let이라는 변수 선언 키워드가 있기 때문입니다. 하지만 var를 사용하지 않는 이유가 그저 오래된 문법이라서일까요? var와 const, let의 차이점을 알아보며 var가 외면받는 이유에 대해 알아보겠습니다.
프로그래밍에서 변수란?
프로그래밍에서 변수란 하나의 값을 저장하기 위해 이름이 붙은 메모리 공간을 뜻합니다.
javascript var password = 123456;
위와 같이 변수를 선언하고 값을 할당하면 3단계에 걸쳐 변수에 값이 저장됩니다.
- 선언 : 자바스크립트 엔진이 변수의 식별자를 인식하는 단계입니다. 식별자만 인식할 뿐 값이 저장되지는 않습니다.
- 초기화 : 변수 식별자가 인식된 후, 메모리 공간을 확보하고 초기값을 설정하는 단계입니다. 이 때 식별자는 확보된 메모리 공간의 주소값을 가르킵니다. 초기 값이 명시되어 있지 않은 경우 undefined로 초기화 됩니다.
- 할당 : 식별자가 가르키고 있는 주소의 메모리 공간에 값을 저장하는 단계입니다.
var와 const, let의 차이
이제 자바스크립트에서 변수를 선언할 때 사용하는 세가지 키워드 var와 const,let의 차이점에 대해 알아봅시다.
1. 재선언 , 재할당 가능 여부
재선언과 재할당이란?
재선언 : 이미 선언된 변수 식별자를 다시 사용하여 변수를 선언하는 것을 의미합니다.
javascript var password = 123456; var password = 654321; // 이미 사용된 password 식별자를 사용하여 변수를 재선언 console.log(654321); // 재선언 시 할당된 654321이 출력된다.
재할당 : 변수에 값을 다시 할당하는 것을 의미합니다.
javascript var password = 123456; password = 654321; // password 값에 값을 재할당 console.log(654321); // 재할당 된 654321 값이 출력된다.
var와 const, let의 재선언, 재할당 여부는 아래와 같습니다.
변수 선언 키워드 | 재선언 | 재할당 |
---|---|---|
var | O | O |
const (ES6) | X | X |
let (ES6) | X | O |
- var : 재선언과 재할당 모두 가능합니다.
- const : 재선언과 재할당 모두 불가능합니다. 값이 변하지 않으니, 선언과 동시에 값이 할당되어야 합니다. 재할당이 허용되지 않으므로 상수(constance)라고도 합니다.
- let : 재할당만 가능하고 재선언은 허용되지 않습니다.
그래서 let이 무슨 뜻이냐고요...
재선언의 위험성 🚨
javascript var password = 123456; // 1000 lines later ...🐌 var password = 654321; // 코드가 너무 길어져 실수로 위에 선언된 password를 덮어쓴다면?!😱
위와 같이 코드가 길어지거나 다른 개발자와 협업하여 일하는 경우 기존에 변수가 재선언되어 덮어써진다면 생각지 못한 에러가 발생하겠죠? 이런 상황을 미연에 방지하기 위해 재선언을 허용하지 않는 키워드인 const와 let을 사용하는 것이 좋습니다.
어떤 상황에서 const를 쓰고 let을 써야할까요?🤔
변수에 값을 재할당해야 할 이유가 없는 모든 경우에 const를 쓰는 것이 바람직합니다. const는 재할당조차 허용하지 않는 가장 엄격한 키워드이기 때문에 의도치 않게 값을 변경시키는 일이 발생하지 않기 때문입니다.
javascript const password = 123456; password = 654321; // TypeError: Assignment to constant variable
부득이하게 값이 재할당 되어야 할 때는 let을 사용하면 됩니다. 저는 주로 if문, switch문 같은 조건문에서 조건에 따라 값을 다르게 할당해야하는 경우에 사용했습니다.
(만약문이라고 쓰고 5초간 뭐가 이상한지 몰랐던 사람이 바로 저예요)
2. 스코프 (scope)
스코프란?
스코프(scope)는 변수에 접근할 수 있는 유효 범위를 뜻합니다.
전역 스코프 (Global Scope) : 함수나 블럭 바깥의 최상위 공간입니다. 전역 스코프에 선언된 변수는 어디서나 접근 가능하며 이를 전역 변수라고 합니다. 지역 스코프 (Local Scope) : 함수나 블럭 내부를 뜻합니다. 지역 스코프에 선언된 변수는 해당 스코프 내에서만 접근 가능하여 이를 지역 변수라고 합니다.
- 전역 스코프에 선언된 변수는 어디서나 접근 가능합니다.
- 지역 스코프에 선언된 변수는 전역 스코프에서 접근할 수 없습니다.
(전역 스코프에서 outerFunction에 선언된 outerVar에 접근할 수 없음)- 함수가 중첩된 경우 하위 함수에서는 상위 함수에 선언된 변수에 접근할 수 있지만 외부 함수에서 내부 함수에 선언된 변수에 접근할 수 없습니다.
(outerFunction에서 innerFunction에 선언된 innerVar에 접근할 수 없음)- 변수에 접근할 때 함수 내부에 선언되지 않았다면 상위 스코프로 올라가서 찾습니다. (스코프 체인)
(innerFunction은 outerFunction, 전역 스코프에 있는 변수에 모두 접근이 가능)
var와 const, let의 스코프 차이
- var의 스코프 : 함수 레벨 (function level)
- const, let의 스코프 : 블럭 레벨 (block level)
함수 스코프의 단점
함수 선언 시에 블럭이 사용되니 둘이 비슷한 것이 아닌가? 라는 생각이 들수도 있지만 함수 레벨의 유효 범위에서는 if문, for문처럼 함수가 아닌 문(statement) 안에서 선언한 변수가 전역변수가 되어 코드 여기저기서 참조할 수 있게 될뿐더러 다른 변수와 충돌할 가능성이 있습니다. 때문에 안정적이고 예측 가능한 변수 사용을 위해 블록 스코프인 const와 let을 사용하는 것이 좋습니다.
javascript for (var i = 0; i < 10; i++) { var a = "a"; } console.log(i); // 10 => 루프 헤더에 선언된 i에 접근 가능 console.log(a); // 'a' => for문 안에 선언된 a에도 접근 가능 if (false) { var b = "b"; } console.log(b); // undefined => if문의 조건이 거짓이므로 값을 할당되지 않았지만 전역변수이기에 접근은 가능하다🙄
3. 호이스팅 (Hoisting) 현상
자바스크립트는 런타임 환경에서 코드를 한 줄 한 줄 읽어내려가는 인터프리터 언어이다.
자바스트립트를 공부하면 제일 처음 배우게 되는 자바스크립트의 특징입니다. (근데 이제 공부할 수록 가장 큰 혼란을 주는)
위의 자바스크립트의 특징을 잘 기억해둔 후 아래 코드를 살펴봅시다.
javascript console.log(password); //undefined var password = 123456;
위 코드는 에러가 나야할 것 같지만 undefined가 출력될 뿐 에러 없이 코드가 돌아갑니다. (어째서.. 🤦)
이는 “끌어올리다”라는 뜻을 가진 호이스팅으로 인해 나타나는 현상으로 자바스크립트 엔진이 코드를 실행 하기 전에 코드의 변수, 함수, 클래스의 선언부를 스코프의 최상단으로 끌어올리는 것처럼 동작하는 현상을 말합니다. (함수 선언문 위에서 함수 실행이 가능한 이유도 호이스팅 때문이죠.)
var의 경우 호이스팅 시 선언과 동시에 undefined로 초기화되기 때문에 변수 선언문 이전에 변수를 참조해도 에러 없이 undefined 값을 얻을 수 있게 됩니다.
var의 호이스팅
- 초기화 된 undefined 출력 😱
- 선언 후 값을 할당하지 않았으므로 undefined로 초기화
- undefined 출력
- 123456 할당
- 123456 출력
그럼 const와 let은 어떨까요?
ReferenceError: Cannot access 'userName' before initialization
‘userName’이 초기화되기 전에 접근할 수 없다는 참조 에러가 뜨게 됩니다. 해당 코드 라인이 실행되는 시점에서 선언하지 않는 변수에 접근할 수 없는 게 우리가 예측할 수 있는 동작이므로 안심이 됩니다. 그런데 한 가지 의문인 점은 왜 ReferenceError: userName is not defined가 아닌 걸까요? 그 이유는 const와 let 또한 호이스팅 현상이 발생하기 때문입니다. 다만 var와의 차이점은 const와 let은 호이스팅과 동시에 변수가 초기화 되지 않는다는 것입니다. TDZ(Temporal Dead Zone), 일시적 사각지대라고 하여 변수 선언이 인식된 후 변수가 초기화 되기 전까지의 구간에서 const와 let 키워드로 선언한 변수에 접근하면 참조 에러가 발생하게 됩니다.
const와 let으로 선언한 변수는 자바스크립트 엔진이 변수 선언문에 도달했을 때 초기화되고 접근이 가능해집니다. 이때 const는 재할당이 불가능하기 때문에 초기화와 동시에 값이 할당되어야 합니다. let은 할당 값이 없다면 undefined로 초기화됩니다.
const의 호이스팅
- 변수 선언이 끌어올려진 후 초기화 되지 않아 접근할 수 없음
(ReferenceError: Cannot access 'password' before initialization)- 값을 재할당 할 수 없는 상수(constant)이기에 초기화와 동시에 할당
- 할당된 값 123456 출력
let의 호이스팅
- 변수 선언이 끌어올려진 후 초기화 되지 않아 접근할 수 없음
(ReferenceError: Cannot access 'password' before initialization)- 선언 후 값을 할당하지 않으면 undefined로 초기화
- 초기화 된 undefined 출력
- 값 123456 할당
- 할당된 123456 출력
var를 지양해야 하는 이유
- 재선언이 가능해 기존 선언을 덮어 쓸 수 있는 위험이 있습니다.
- 호이스팅으로 인하여 변수 선언문 이전에 변수에 접근 가능한 예기치 못한 동작이 발생합니다.
- 함수 스코프이기 때문에 if문, for문 등 문에서 쓰인 변수가 전역 변수가 되어 코드 여기 저기서 접근이 가능하고 변수 간 충돌이 발생할 수 위험이 있습니다.
=> 따라서 코드의 예측 가능성과 안정성을 저하시키는 var를 지양하고 이를 보완하기 위해 나온 const와 let을 사용하는 것을 적극 권장합니다👍
포스팅을 마치며
이렇게 var의 특징과 그 특징으로부터 발생하는 위험성에 대해 알아보았습니다. var를 사용하면 코드가 일단 돌아는가지만 왜 돌아가는지 혼란스럽고, 왜 여기서 내가 예상한 동작과 다른 버그가 발생하는지 파악하는 데 시간을 허비하게 될 가능성이 크겠죠. 그렇기 때문에 요즘에는 var를 사용하지 않는 추세입니다. 다만 사용하지 않으니 알아두지 않는 것이 아니라 어떤 특징을 가지고 있었는지, 그 특징으로부터 어떤 불편함이 발생했는지, 불편을 해소하기 위해 어떻게 해야 하는지 알아두는 것이 중요하다고 생각합니다. 그리고 ES6가 나온 이후에 개발을 시작한 것이 얼마나 큰 행복인지도요🥹