개발

[JavaScript] var, const, let 이야기

베르월드 2023. 1. 9. 10:36

ES5까지의 자바스크립트의 변수 선언은 var 키워드만으로 변수 선언이 가능하였지만,
ES6에서 const, let 키워드가 새로 등장하게 되었다. 어째서 등장하게 되었을까?
그리고 많은 자바스크립트 강의를 보면 "var를 지양해야 한다"라는 말을 많이 들어보았을 것이다.

이번 포스팅으로 어째서 const와 let이 등장하게 되었으며, var를 지양해야 하는 이유를 알아보고자 한다.

 

var 키워드의 변수의 문제점


var 키워드로 선언한 변수의 문제점은 크게 3가지로 분류할 수 있다.

1. 변수 중복 선언 허용

2. 함수 레벨 스코프

3. 변수 호이스팅

차례대로 어떤 것이 문제인지 알아보도록 하자.

 

1.  변수 중복 선언 허용

var 키워드는 변수 중복 선언이 허용된다. 다음 예제를 보고 이해해 보자

var foo = 'bar'
var foo = 'bar2'
console.log(foo) // bar2

위의 예제를 보면 같은 foo라는 같은 변수명으로 var 키워드로 선언했지만 에러가 발생하지 않고 먼저 선언된 변수의 값이 변경되고 있다.


만약 실무에서 아래와 같은 상황이 발생했다고 가정해 보자.

// user 변수의 유저 데이터를 사용하고 있었다.
var user = {
   name : '홍길동'
   ...
}

...
...
...

// 하지만 다른 개발자가 user 변수를 사용하는지 체크를 못하고 user 변수에 값을 새롭게 할당을 했다.
var user = '이순신'

위 예제처럼 중복선언으로 인해 기존에 사용하던 변수가 재 선언이 되어 만들어 놓은 소스코드가 엉망진창으로 변경될 수 있고, 사이드 이펙트가 발생할 확률이 높아진다.

 

 

2. 함수 레벨 스코프

var 키워드로 선언한 변수는 함수 내에서만 지역스코프로 인정받는다.

함수 외부에서 var 키워드로 선언한 변수는 전부 전역 변수가 되어 살아있게 된다.

var bar = 1

function foo () {
   var bar = 2
   console.log(bar)
}

foo() // 2
console.log(bar) // 1

위의 예제를 보면 var키워드로 전역에 bar 변수를 선언하였고 foo 함수 안에 bar 변수를 중복 선언 했지만
var키워드는 함수 레벨 스코프라 함수 안에 호출된 bar 변수가 전역에 선언한 bar 변수에 영향을 주지 않는다.

하지만 함수 이외에 var 키워드로 선언된 변수는 모두 전역 변수가 된다고 하였다. 다음 예제를 보자.

 

var i = 1;

// i 변수를 for문에서 중복선언 하였다.
for(var i = 0; i < 5; i++){
	console.log(i) // 0 1 2 3 4 
}

console.log(i) // 5

위의 예제를 보면 var 키워드로 전역에 i 변수를 선언했고, for문에서 var 키워드로 i 변수를 중복 선언 했다.

결과를 보면 최초로 선언된 i 변수가 for문의 i 변수로 값이 변경되어 전역으로 저장된 i 변수의 값이 5로 변경되었다.
위와 같이 함수 이외에 사용된 var 키워드 변수는 전부 전역변수가 되어 의도치 않게 중복 선언이 되거나 쓸모없는 변수가 전역변수로 살아있게 된다.

 

3. 변수 호이스팅

호이스팅(hoisting)이란, 우선 자바스크립트 엔진이 동작하는 순서를 알아야 하는데 자바스크립트 엔진이 런타임 전 소스코드 평가 과정을 거치게 된다. 이때 먼저 소스코드에서 선언문(변수 선언문, 함수 선언문)들을 모두 찾아 변수 초기화를 실행하는데 변수의 값은 undefined로 선언된다. 이 과정을 호이스팅이라고 한다. 아래 예제를 보자.

console.log(foo) // undefined
console.log(bar) // Uncaught ReferenceError: bar is not defined

var foo = 'bar'

위 예제에서 console.log 아래에서 var 키워드로 선언한 foo 변수는 undefined가 출력되고 선언하지 않은 bar 변수는 ReferenceError가 발생한다. 이처럼 var 키워드를 사용하면 변수 선언 전에 참조가 가능해 프로그램의 흐름과 맞지 않고, 가독성을 떨어뜨리며, 오류를 발생할 여지를 남기게 된다.

 

 

그래서 const와 let은?


const와 let은 ES6에서 var 키워드의 단점을 보완하기 위해 도입됐다 어떤 차이가 있는지 알아보자.

 

1.  변수 중복 선언 금지

var 키워드로 동일한 변수명으로 선언 시 에러가 발생하지 않았다. 하지만 const와 let 키워드는 중복선언 했을 시 문법 에러가 발생한다. 아래 예제를 보자.

var foo = 'bar'
var foo = 'bar2'

const foo2 = 'bar'
const foo2 = 'bar2' // Uncaught SyntaxError: Identifier 'foo2' has already been declared

let foo3 = 'bar'
let foo3 = 'bar2' // Uncaught SyntaxError: Identifier 'foo3' has already been declared

var 키워드로 중복 선언한 변수는 아무 에러를 발생하지 않지만 const, let 키워드는 에러를 발생시켜 중복 선언이 되었다고 알려줘 잘못된 변수 선언을 줄일 수 있고, 안전한 코딩을 할 수 있게 된다.

 

2.  블록 레벨 스코프

var 키워드는 함수의 코드 블록 내에서만 지역 스코프로 인정받았고, 이외에는 전부 전역 변수로 저장되었다. 하지만 const, let 키워드는 모든 코드 블록에서 지역 스코프로 인정되는 블록 레벨 스코프이다. 아래 예제를 보자.

let foo = 1 // 전역변수
const bar = 1 // 전역변수

{
   let foo = 2 // 지역변수
   const bar = 2 // 지역변수
    
   console.log(foo) // 2
   console.log(bar) // 2
}

console.log(foo) // 1
console.log(bar) // 1

위 예제를 보면 코드블록에서 변수를 중복 선언했지만 코드블록 내에서만 값이 할당되고 전역에서 선언된 변수와 별개의 변수로 선언된다. 이에 따라 전역에선 지역변수를 참조할 수 없다. 다음 예제를 보자.

{
   let foo = 1
   const bar = 2
}

console.log(foo) // Uncaught ReferenceError: foo is not defined
console.log(bar) // Uncaught ReferenceError: bar is not defined

위의 예제와 같이 코드블록에서 선언한 변수를 전역에서 참조하였지만 ReferenceError가 발생하여 참조할 수 없게 된다.

 

3.  변수 호이스팅

var 키워드는 변수 호이스팅이 되어 변수 할당 전에 참조가 가능했다. const, let 키워드로 선언한 변수는 변수 호이스팅이 발생하지 않는 것처럼 동작시켜준다. 다음 예제를 보자.

console.log(foo) // Uncaught ReferenceError: foo is not defined
console.log(bar) // Uncaught ReferenceError: bar is not defined
let foo = 1
const bar = 2

위 예제처럼 변수 선언 전에 참조를 했을 때 ReferenceError가 발생하여 참조할 수 없게 된다. 그런데 왜 변수 호이스팅이 발생하지 않는 것처럼 동작 시켜준다라고 했을까? 

 

선언단계 초기화단계
할당단계

그림 1. var 키워드로 선언한 변수의 생명주기

 

선언단계
일시적 사각지대(TDZ)
초기화 단계
할당 단계

그림 2. let 키워드로 선언한 변수의 생명주기

 

var 키워드는 선언과 초기화가 동시에 이루어지고 할당을 하지만 let 키워드로 선언과 초기화 단계가 분리되어 진행되어 초기화는 변수 선언문에 도달했을 때 이루어지게 된다. 그래서 let 키워드로 선언한 변수는 호이스팅이 이루어지지 않는 것처럼 보이지만 그렇지 않다. 다음 예제를 보자.

let foo = 1;

{
   console.log(foo); // Uncaught ReferenceError: Cannot access 'foo' before initialization
   let foo = 2;
}

위 예제를 보면 변수 호이스팅이 발생하지 않는다면 전역에 선언한 foo 변수가 출력 되어야 하는데 ReferenceError에러가 발생하고 있다. 이 예제를 보면 let 키워드로 선언한 변수도 여전히 호이스팅이 발생하기 때문에 참조 에러가 발생하는 것으로 볼 수 있다.

 

const 키워드로 선언한 변수는 let 키워드와 다르게 반드시 선언과 동시에 초기화해야 한다. 다음 예제를 보자.

const foo = 1;
const bar; // Uncaught SyntaxError: Missing initializer in const declaration

위 예제를 보면 초기화를 하지 않으면 SyntaxError가 발생하여 변수를 사용할 수 없게 된다. 또한 let 키워드와 동일하게 변수 호이스팅이 발생하지 않는 것처럼 동작한다.

{
   console.log(foo); // Uncaught ReferenceError: Cannot access 'foo' before initialization
   const foo = 1;
   console.log(foo); // 1
}

// 블록 레벨 스코프를 가지고 있어 참조할 수 없다.
console.log(foo); // Uncaught ReferenceError: foo is not defined

 

결론


지금까지 var 키워드의 단점과 const, let 키워드에서 어떻게 보완했는지 알아보았다.

var 키워드는 내가 원하지 않는 결과인 사이드 이펙트가 발생하기 쉽다. 

var 키워드를 지양하고 const 키워드와 let 키워드를 잘 활용하여 클린코드를 작성해보자!

 

참고자료 - 이웅모 『모던 자바스크립트 Deep Dive』 위키북스