자바스크립트 변수 선언 var, let, const 뭐가 다를까?

개발/javascript&typescript1 2025. 12. 18. 13:01

JavaScript var, let, const 차이와 사용 기준 정리

자바스크립트 초보 때는 변수 선언이 그냥 "값 넣는 통" 정도로 느껴지는데, 프로젝트를 하다 보면 var / let / const의 차이가 생각보다 자주 문제를 만들기도 합니다.
특히 스코프(범위), 호이스팅(끌어올림), 재선언/재할당 가능 여부 때문에 코드가 "돌아가긴 하는데 불안한" 상태가 되는 경우가 많아서요.
이번 글에서는 세 가지 차이를 한 번에 정리하고, 실제로 어떤 기준으로 쓰면 좋은지까지 같이 정리해보겠습니다.

 


 

1) 한 줄로 먼저 결론

  • const: 기본값(우선순위 1).
    "재할당이 없어야 하는 값"에 사용합니다.
  • let: 필요할 때만(우선순위 2).
    "재할당이 필요한 값"에 사용합니다.
  • var: 가능하면 피합니다(우선순위 3).
    레거시 코드 유지보수 외에는 권장하지 않습니다.

 


 

2) 핵심 차이 비교표

항목
var let const
스코프(scope) 함수 스코프(function scope) 블록 스코프(block scope) 블록 스코프(block scope)
재선언 가능 불가능 불가능
재할당 가능 가능 불가능
호이스팅 됨(초기값 undefined로 접근 가능) 됨(하지만 TDZ 때문에 선언 전 접근 불가) 됨(하지만 TDZ 때문에 선언 전 접근 불가)
전역 객체(window/globalThis) 프로퍼티 전역 var는 붙는 경우가 많음 보통 안 붙음 보통 안 붙음

여기서 실무에서 가장 크게 체감되는 건 스코프와 재선언/재할당, 그리고 TDZ(Temporal Dead Zone)입니다.


 

3) 스코프 차이: var는 "함수 기준", let/const는 "블록 기준"

var: 함수 스코프라서 블록을 무시하는 느낌이 납니다

function test() {
  if (true) {
    var x = 10;
  }
  console.log(x); // 10 (if 블록 밖인데도 접근됨)
}
test();

 

let/const: 블록 스코프라서 더 예측 가능합니다

function test() {
  if (true) {
    let y = 10;
    const z = 20;
  }
  console.log(y); // ReferenceError
  console.log(z); // ReferenceError
}
test();

이 차이 때문에, var를 쓰면 “블록 안에서만 쓰려고 선언했는데 밖에서 살아있어 버리는” 상황이 생길 수 있습니다.

 


 

4) 호이스팅 차이: var는 선언 전에 접근하면 undefined, let/const는 에러

var: 선언 전에 읽혀버리는 게 문제를 숨깁니다

console.log(a); // undefined
var a = 1;

 

let/const: 선언 전에 접근하면 바로 에러(실수를 빨리 잡게 해줌)

console.log(b); // ReferenceError
let b = 1;

이 구간을 TDZ(Temporal Dead Zone)라고 부르는데, 쉽게 말해 “선언은 끌어올려지지만, 초기화 전에는 접근을 막는다”라고 이해하시면 됩니다.


 

5) 재선언/재할당 차이: const는 재할당 불가, let은 가능, var는 둘 다 가능(위험)

var는 재선언까지 가능해서 실수 유발이 큽니다

var n = 1;
var n = 2; // 에러가 안 남
console.log(n); // 2

 

let/const는 재선언이 막혀서 실수를 줄여줍니다

let m = 1;
let m = 2; // SyntaxError

 

재할당은 let만 가능

let count = 0;
count = count + 1; // OK

const pi = 3.14;
pi = 3.14159; // TypeError

 


 

6) const인데 객체/배열은 "내용 변경"이 가능하다는 점

여기서 const를 처음 쓰면 헷갈리는 부분이 하나 있습니다. const는 "값 자체를 바꾸는 것(재할당)"만 막고, 객체/배열 내부 변경은 막지 않습니다.

const user = { name: "kim" };
user.name = "lee"; // OK (객체 내부 변경)

user = { name: "park" }; // TypeError (재할당)

 

배열도 마찬가지입니다.

const arr = [1, 2, 3];
arr.push(4); // OK
arr = [1, 2]; // TypeError

 

정말 내부 변경까지 막고 싶으면 freeze를 쓰기도 하지만, 보통은 필요할 때만 고민하시면 됩니다.

 



7) 실무 사용 기준: 이렇게 정하면 깔끔합니다

기준 1) 기본은 const로 시작

  • "이 값이 중간에 다른 값으로 바뀌어야 하나?"를 먼저 생각합니다.
  • 아니라면 const가 기본입니다.
const API_URL = "https://example.com";
const userId = getUserId();

 

기준 2) 재할당이 필요하면 let

  • 반복문 카운터, 상태값 누적, 조건에 따라 값이 바뀌는 변수 등에 사용합니다
let total = 0;
for (const price of prices) {
  total += price;
}

 

기준 3) var는 새 코드에서 사용하지 않기

  • 구형 코드(레거시) 유지보수 중 var를 마주칠 수는 있는데,
  • 신규 작성에서는 var를 쓰는 순간 "스코프/호이스팅 관련 이슈"가 섞일 가능성이 커집니다.

 

8) 자주 나오는 실수 패턴 2개

(1) for문에서 var를 쓰면 비동기/클로저에서 꼬이는 경우

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}
// 3 3 3

 

let을 쓰면 각 반복마다 블록 스코프가 분리돼서 기대대로 동작합니다.

for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}
// 0 1 2

 

(2) 선언 전에 접근해도 에러가 안 나서 버그가 숨는 경우(var)

function run() {
  if (!flag) {
    // ...
  }
  var flag = true; // 선언이 끌어올려져서 flag가 undefined로 평가될 수 있음
}

 

이런 패턴은 let/const로 바꾸면 대부분 즉시 잡힙니다.

반응형
admin