Variable Declarations

JavaScript에서 변수 선언(Variable Declarations)은 프로그램에서 데이터를 저장하고 참조하는 데 사용되는 중요한 개념이다. 변수 선언 방식에 따라 변수의 **유효 범위(Scope)**와 재할당 가능 여부 등이 결정된다.
ES6(ECMAScript 2015) 이전에는 var 키워드만을 사용하여 변수를 선언했으나, 이후 letconst 키워드가 도입되어 보다 다양한 변수 선언이 가능해졌다.

JavaScript에서는 주로 세 가지 키워드를 사용하여 변수를 선언한다:

  1. var 키워드 사용
  2. let 키워드 사용
  3. const 키워드 사용

Var 키워드

var는 JavaScript의 가장 오래된 변수 선언 방식이다.

  • 함수 레벨 스코프(Function-level scope): 함수 내부에서 선언된 변수는 함수 내부에서만 유효하며, 함수 외부에서는 접근할 수 없다.
    • 함수 내부에서 선언되면 함수 내부에서만 접근 가능
    • 함수 외부에서 선언되면 전역 변수가 됨
  • 중복 선언 가능: 같은 이름의 변수를 여러 번 선언해도 에러가 발생하지 않는다.
  • 변수 호이스팅(Variable Hoisting): 변수 선언이 스코프의 최상단으로 끌어올려지는 특성이 있다.
    예시:
1
2
3
4
5
6
7
8
9
function example() {
    var x = 1;
    if (true) {
        var x = 2; // 같은 변수 x를 다시 선언
        console.log(x); // 출력: 2
    }
    console.log(x); // 출력: 2
}
example();

위 코드에서 var로 선언된 변수 x는 함수 전체에서 동일한 스코프를 공유하므로, 블록 내부와 외부에서 값이 변경된다.

Let 키워드

let은 ES6에서 도입된 변수 선언 방식이다.

  • 블록 레벨 스코프(Block-level scope): 모든 코드 블록(예: {}, if, for 등) 내에서 선언된 변수는 해당 블록 내에서만 유효하다.
    • 선언된 블록 내에서만 접근 가능
  • 중복 선언 불가: 같은 이름의 변수를 동일한 스코프 내에서 다시 선언하면 에러가 발생한다.
  • 변수 호이스팅 발생하지만 초기화 이전에는 접근 불가: let으로 선언된 변수는 호이스팅되지만, 초기화되기 전까지는 접근할 수 없어 ReferenceError가 발생한다.
  • 선언 전에 사용하면 일시적 사각지대(Temporal Dead Zone) 오류 발생
    예시:
1
2
3
4
5
6
7
8
9
function example() {
    let y = 1;
    if (true) {
        let y = 2; // 블록 내부의 새로운 변수 y
        console.log(y); // 출력: 2
    }
    console.log(y); // 출력: 1
}
example();

위 코드에서 let으로 선언된 변수 y는 블록마다 독립적인 스코프를 가지므로, 블록 내부와 외부의 y는 서로 다른 변수이다.

Const 키워드

const도 ES6에서 도입되었으며, 상수를 선언하는 데 사용된다.

  • 블록 레벨 스코프(Block-level scope): let과 동일하게 블록 내에서만 유효하다.
  • 중복 선언 불가: 같은 이름의 변수를 다시 선언하면 에러가 발생한다.
  • 선언과 동시에 초기화 필수: const로 선언한 변수는 선언 시 반드시 값을 할당해야 한다.
  • 재할당 불가: 한 번 할당된 값을 변경할 수 없다.
  • 객체와 배열의 내용은 변경 가능: 객체나 배열 자체는 변경할 수 없지만, 그 내부 속성이나 요소는 변경할 수 있다.
  • let과 마찬가지로 일시적 사각지대가 있음
    예시:
1
2
3
4
5
6
7
8
const PI = 3.14;
// PI = 3.14159; // TypeError: Assignment to constant variable

const user = { name: "홍길동", age: 20 };
user.age = 21; // 객체의 속성은 변경 가능
console.log(user); // { name: "홍길동", age: 21 }

// user = {}; // TypeError: Assignment to constant variable

변수 선언 방식 비교

특성varletconst
스코프함수 스코프블록 스코프블록 스코프
호이스팅선언과 초기화가 호이스팅됨선언만 호이스팅됨 (TDZ)선언만 호이스팅됨 (TDZ)
재선언가능불가능불가능
재할당가능가능불가능
선언 전 사용undefinedReferenceErrorReferenceError

varlet/const의 블록 스코프 차이 비교

특성varlet / const
스코프함수 스코프블록 스코프
블록 내부 접근가능불가능 (오직 블록 내부에서만 접근 가능)
중복 선언가능불가능
호이스팅선언+초기화(undefined)선언만 (초기화는 선언 이후)
TDZ 발생없음있음 (선언 전 접근 시 ReferenceError)

var vs. let/const 정리

  • var는 블록을 무시하고 함수 전체에서 접근 가능 → 의도치 않은 변수 변경 가능성 증가.
  • letconst는 블록 단위로 변수를 제한 → 코드의 안정성을 높이고 예측 가능한 동작 보장.
  • const는 재할당이 불가능하여, 변경할 필요 없는 값에 사용.

변수 선언의 모범 사례

  1. 기본적으로 const 사용: 변수의 값이 변경되지 않을 경우 (대부분의 경우) const를 사용한다.
  2. 필요한 경우에만 let 사용: 변수의 값이 나중에 변경되어야 할 경우에만 let을 사용한다.
  3. var 사용 지양: 최신 JavaScript에서는 var 대신 letconst를 사용하는 것이 권장된다.
  4. 의미 있는 이름 사용: 변수 이름은 그 용도를 명확히 나타내야 한다.
  5. 변수는 사용 직전에 선언: 변수를 사용하기 직전에 선언하여 가독성을 높인다.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 권장 방식
function processUser(userData) {
  const user = {
    name: userData.name,
    age: userData.age
  };
  
  let score = 0; // 변경될 값이므로 let 사용
  
  if (userData.premium) {
    const PREMIUM_BONUS = 10; // 상수는 대문자로
    score += PREMIUM_BONUS;
  }
  
  for (let i = 0; i < userData.achievements.length; i++) {
    const achievement = userData.achievements[i];
    score += achievement.points;
  }
  
  return {
    user,
    finalScore: score
  };
}

변수 선언 시 고려사항

  1. 스코프: var는 함수 스코프, let과 const는 블록 스코프를 가진다.
  2. 호이스팅: var는 선언과 초기화가 호이스팅되지만, let과 const는 선언만 호이스팅된다.
  3. 재할당: var와 let은 재할당이 가능하지만, const는 불가능하다.
  4. 중복 선언: var만 중복 선언이 가능하다.

최신 JavaScript에서의 권장사항

  1. 기본적으로 const를 사용하여 변수를 선언한다.
  2. 재할당이 필요한 경우에만 let을 사용한다.
  3. var의 사용은 가급적 피하고, 레거시 코드를 다룰 때만 사용한다.

실제 사용 예제와 모범 사례

스코프 차이 이해하기

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
function scopeExample() {
  var functionScoped = "I am function scoped";
  
  if (true) {
    var alsoFunctionScoped = "I am also function scoped";
    let blockScoped = "I am block scoped";
    const alsoBlockScoped = "I am also block scoped";
    
    console.log(functionScoped); // 접근 가능
    console.log(alsoFunctionScoped); // 접근 가능
    console.log(blockScoped); // 접근 가능
    console.log(alsoBlockScoped); // 접근 가능
  }
  
  console.log(functionScoped); // 접근 가능
  console.log(alsoFunctionScoped); // 접근 가능
  // console.log(blockScoped); // ReferenceError
  // console.log(alsoBlockScoped); // ReferenceError
}

루프에서의 변수 선언

var를 사용할 때 발생할 수 있는 문제와 let을 사용했을 때의 차이:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// var 사용 시 문제
function varInLoop() {
  var funcs = [];
  
  for (var i = 0; i < 3; i++) {
    funcs.push(function() {
      console.log(i);
    });
  }
  
  // 모두 3을 출력
  funcs.forEach(function(func) {
    func();
  });
}

// let 사용 시 해결
function letInLoop() {
  var funcs = [];
  
  for (let i = 0; i < 3; i++) {
    funcs.push(function() {
      console.log(i);
    });
  }
  
  // 0, 1, 2를 출력
  funcs.forEach(function(func) {
    func();
  });
}

상수와 객체 사용

const로 선언한 객체의 내부 속성 변경:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
const CONFIG = {
  apiUrl: "https://api.example.com",
  timeout: 3000,
  maxRetries: 3
};

// 내부 속성은 변경 가능
CONFIG.timeout = 5000;
CONFIG.newProperty = "새 속성";

// 전체 객체 재할당은 불가
// CONFIG = { newConfig: true }; // TypeError

// 객체를 완전히 불변으로 만들려면
const FROZEN_CONFIG = Object.freeze({
  apiUrl: "https://api.example.com",
  timeout: 3000
});

// FROZEN_CONFIG.timeout = 5000; // 엄격 모드에서는 에러, 아니면 무시됨

참고 및 출처