본문 바로가기
💻 웹 개발/JavaScript

var, let, const의 5가지 차이점 (재선언, 재할당, 스코프, 호이스팅, 전역 객체)

by llddang 2025. 2. 11.

JavaScript에서 var, let, const 키워드를 이용하여 변수를 선언할 수 있다. 
기존에는 var 키워드가 유일한 변수 선언 키워드였다. 하지만 var 키워드에는 여러 문제점을 가지고 있었고, 이를 해결하기 위해서 ES6(ECMAScript2015)에서 let과 const 키워드가 도입되었다.

 

오늘은 var 키워드의 문제점을 살펴보면서 let과 const 키워드에서는 어떻게 달라졌는지 차이점에 대해 알아보도록 하겠다!

 

 

1. 재선언

재선언이란 동일한 스코프 내에서 동일한 이름의 변수를 다시 선언하는 것을 의미한다.

 

var user = { firstName: "Gil-dong", lastName: "Hong" };
var user = { name: "Hong Gil-dong" };

console.log(user); // { name: "Hong Gil-dong" };

 

위 코드와 같이, var 키워드는 동일한 변수명을 중복하여 선언할 수 있다.

이는 잘못하여 동일한 변수명을 작성하여 이전 변수를 덮어씌워 예기치 못 한 에러를 발생시킬 수 있다.

 

하지만 let 과 const 키워드에서 동일한 이름의 변수를 중복 선언할 수 없다!

let x = 1;
const y = 2;

let x = 11;   // SyntaxError: Identifier 'x' has already been declared
const y = 12; // SyntaxError: Identifier 'y' has already been declared

 

let 과 const 키워드로 선언한 변수를 동일한 이름의 변수로 재선언하려고 하면 SyntaxError가 나게된다.

 

   재선언 ( 동일한 변수명으로 다시 선언하는 것 )
var 가능 : 새로운 값으로 초기화 됨.
let  불가능 : SyntaxError 발생
const 불가능 : SyntaxError 발생

 

 

2. 변수 호이스팅

자바스크립트 엔진은 소스코드는 평가 단계와 실행 단계로 나뉘어 처리된다.
평가 단계에서 소스코드 실행에서 변수/함수 등의 선언문을 실행하여 필요한 정보를 저장한다.
실행 단계에서는 해당 정보를 가지고 실제로 코드를 실행한다.

평가 단계에서 선언문들이 먼저 실행되기 때문에 코드 실행에 들어가기 전에 선언문이 스코프의 최상단으로 끌어올려진 것처럼 보인다.
이렇게 선언문이 스코프의 최상단으로 끌어올려지는 것처럼 보이는 현상을 호이스팅이라고 한다.

 

그럼 호이스팅이 왜 문제인지 살펴보자.

console.log(user); // undefined;

var user = { name: "Hong Gil-Dong" };

 

위와 같이 작성된 코드는 자바스크립트 엔진의 작동 방식에 의해 호이스팅이 발생하여 아래와 같은 코드로 해석된다.

 

var user; // 선언문이 스코프의 최상단으로 올려지는 것 처럼

console.log(user); // undefined;

user = { name: "Hong Gil-Dong" };

 

따라서 user는 실제로 밑에서 작성되어서 참조할 수 없지만 호이스팅에 의해 선언된 것으로 판단되어 undefined가 출력되게 된다.

이러한 동작은 코드의 실행 흐름을 예측하기 어렵게 만든다....

 

그럼 let과 const 키워드에서는 어떻게 달라졌는지 살펴보자.

console.log(x); // ReferenceError: Cannot access 'x' before initialization
let x = 1;

console.log(y); // ReferenceError: Cannot access 'y' before initialization
const y = 2;

 

let 과 const 키워드로 선언된 변수를 선언문 이전에 참조하려고 하면 ReferenceError가 나게 된다.

그리고 에러 문구로는 변수가 초기화되기 이전에 참조할 수 없다고 나온다.

 

let과 const 키워드로 선언된 변수에서도 호이스팅이 일어난다.
하지만 선언만 실행되고 일시적 사각 지대(Temporal Dead Zone)가 된다.
이후, 코드가 선언문을 만나 초기화되기 전까지 참조할 수 없.

 

   호이스팅  ( 선언문이 스코프의 최상위로 올려지는 것처럼 보이는 현상 )
var undefined 반환 : 선언문 이전에 참조 가능
let  불가능 : ReferenceError 발생
const 불가능 : ReferenceError 발생

 

 

3. 스코프 범위

스코프란 변수나 함수를 참조할 수 있는 유효한 범위를 의미한다.

 

스코프에는 전역 스코프, 함수 레벨 스코프, 블록 레벨 스코프가 있다.

  • 전역 스코프 : 전역(모든 코드)에서 접근할 수 있는 가장 바깥쪽 스코프
  • 함수 레벨 스코프 : 함수 내부에서 선언된 변수가 갖는 스코프로, 함수 내부에서만 접근 가능
  • 블록 레벨 스코프 : 블록(if, for, switch 등) 내부에서 선언된 변수가 갖는 스코프로, 블록 내부에서만 접근 가능

출처 : 모던 자바스크립트 Deep Dive

 

var 키워드로 선언된 변수는 함수 레벨 스코프를 가진다.
따라서 함수 외부에 작성된 변수는 전역 변수가 되고, 이는 전역 변수를 남발할 가능성을 높인다.
이로 인해 의도치 않게 전역 변수가 중복 선언 되는 경우가 발생한다.

var x = 1;
if (ture) {
  var x = 10;
}

console.log(x); // 10;

 

위의 코드에서 var 변수는 전역에 한 번, if 문안의 블록에서 한 번 선언되었다. 하지만 var 키워드로 선언된 변수는 함수 레벨 스코프이기 때문에 함수 외부에 작성된 변수는 모두 전역 변수로 취급된다.

따라서 if 문 안의 변수는 이전 x를 덮어 씌우게 되고, console.log에서 결국 10이 출력되게 된다.

 

또한 예기치 못한 변수 충돌을 줄이기 위해서 변수의 유효범위가 좁을 수록 좋다.

 

let과 const 키워드로 선언된 변수는 블록 레벨 스코프를 가진다.

let a = 2;
const b = 3;

if(true) {
  let a = 22;
  const b = 33;
  
  console.log(a); // 22
  console.log(b); // 33
}

console.log(a); // 2
console.log(b); // 3

따라서 위의 코드에 작성된 최상위 변수 a,b 와 if 문 블록 안의 a,b 는 다른 변수이고, console.log에서도 다른 값이 나오게 된다!

 

   스코프 범위 ( 변수를 참조할 수 있는 유효 범위)
var 함수 레벨 스코프
let  블럭 레벨 스코프
const 블럭 레벨 스코프

 

4. 전역 객체를 통함 참조

전역 코드에서 var 키워드로 선언된 변수는 전역 환경 레코드에서 객체 환경 레코드가 관리하는 전역 객체의 프로퍼티에 저장된다.

따라서 browser에서는 window 객체, node.js 환경에서는 global 객체의 프로퍼티로 참조가 가능하다.

var nameByVar = "Kim";
console.log(window.nameByVar); // "Kim"

이는 var 변수가 전역 객체의 기존 프로퍼티/메서드와 동일한 네이밍을 사용하여 값을 덮어버릴 수도 있다.

개발자도구 console에서 전역 객체의 값을 덮어버리는 경우

 

하지만 전역 코드에서 let과 const 키워드로 선언된 변수는 전역 환경 레코드의 선언적 환경 레코드에서 관리된다.

따라서 window / global 객체의 프로퍼티로 저장되지 않기 때문에 전역 객체를 통한 참조는 할 수 없다.

let nameByLet = "Kim";
console.log(window.nameByLet); // undefined

const nameByConst = "Kim";
console.log(window.nameByConst); // undefined

 

   전역 객체의 프로퍼티로 참조
var 가능
let  불가능
const 불가능

 

 

5. 재할당

재할당이란 이미 선언된 변수에 새로운 값을 다시 할당하는 것을 의미한다.

 

var user = { firstName: "Gil-dong", lastName: "Hong" };
user = { name: "Hong Gil-dong" };

console.log(user); // { name: "Hong Gil-dong" };

 

위와 같이, var 키워드는 선언된 변수에 값을 다시 할당할 수 있다.

사실 이는 문제가 아니다.
모든 변수를 재할당 할 수 없으면 오히려 매번 새로운 변수를 생성하느라 메모리 낭비가 발생할 것이다.

 

하지만 종종 프로젝트에서 변경되지 않아야 하는, 상수로 선언되어야 하는 변수가 있다.

const로 선언된 변수는 값을 재할당 할 수 없으며 변경되지 않는 값임을 의도한다.

let x = 1;
x = 11;
console.log(x); // 11

const y = 2;
y = 12; // TypeError: Assignment to constant variable.

 

let 키워드로 선언한 변수에는 값을 재할당할 수 있지만, const 키워드로 선언한 변수에 값을 재할당하려고 하면 TypeError가 나게 된다.

 

또한 const 키워드에서 초기화 값을 정의하지 않고 선언할 수 없다.

const a; // SyntaxError: Missing initializer in const declaration

 

하지만 const 키워드로 선언된 변수가 재할당 되지 않는다고 하여 불변이라는 것은 아니다!!

 

만약 const 키워드로 선언된 변수의 자료형이 불변 자료형이 아닌, 참조 자료형이라고 하자.
그렇다면 참조형 데이터 안의 값은 변경할 수 있다.

const user = { firstName: "Gil-Dong", lastName: "Hong" };
user.lastName = "Kim";

console.log(user); // { firstName: "Gil-Dong", lastName: "Kim" };

 

 

 

지금까지 var와 let, const의 차이점에 대해서 알아보았다.

한눈에 보기쉽게 표로 정리하면 아래와 같다.

  재선언 선언문 이전 참조 스코프 범위 재할당 전역 객체를 통한 참조
var 가능 undefined 함수 레벨 스코프 가능 가능
let SyntaxError ReferenceError 블록 레벨 스코프 가능 undefined
const SyntaxError ReferenceError 블록 레벨 스코프 TypeError undefined

 

var에는 여러 문제점 때문에 코드를 작성하는데 더 주의를 기울여야 한다.
가장 주의할게 적은 const 키워드로 변수를 선언한다.

이후 값을 재할당해야한다면 let 키워드로 변수를 바꾸는게 좋을 것 같다!

 

 

퀴즈!

아래의 코드들에서 변수 선언 키워드가 var, let, const 일 때,

1번 출력과 2번 출력에서 어떤 결과가 나올지 생각해보자!

var/let/const a = "global";

function foo() {
  console.log(a);  // 1번 출력
  var/let/const a = "local";
}

foo();
console.log(a); // 2번 출력

 

var/let/const b = 111;

if(true){
  console.log(b); // 1번 출력
  var/let/const b = 222;
}

console.log(b); // 2번 출력

 

var/let/const x = 1;
foo();

for(var x=0; x<10; x++){}

function foo () {
  var x = 1;
  console.log(x); // 1번 출력
}
console.log(x); // 2번 출력

 

정답은... 직접 실행해보기!! 캬햐~!

 

 

참고