JavaScript 변수의 선언과 초기화 완벽 가이드 - var, let, const의 차이점
코드를 작성하다가 이런 경험 해보신 적 있나요? const로 변수를 선언했는데 초기값을 주지 않아서 SyntaxError가 발생했거나, let으로 선언한 변수를 사용하려는데 undefined가 나와서 당황한 적 말이죠.
저도 처음 JavaScript를 배울 때 “선언”과 “초기화”가 뭐가 다른지 잘 몰랐습니다. “그냥 변수를 만드는 거 아닌가?” 하고 생각했죠. 하지만 이 둘의 차이를 이해하는 것은 JavaScript의 동작 방식을 이해하는 핵심입니다.
이 가이드에서는 변수의 선언과 초기화의 차이부터, var, let, const의 동작 방식까지 MDN 공식 문서를 기반으로 깊이 있게 설명합니다.
왜 선언과 초기화의 차이를 알아야 할까요?
1. 예상치 못한 버그 방지
선언과 초기화의 차이를 모르면 변수가 undefined인 이유를 찾느라 시간을 낭비하게 됩니다.
2. 올바른 키워드 선택
언제 var, let, const를 사용해야 하는지 명확하게 알 수 있습니다.
3. 코드 품질 향상
변수의 생명주기를 이해하면 더 안전하고 예측 가능한 코드를 작성할 수 있습니다.
4. 면접 질문 대비
“선언과 초기화의 차이는 무엇인가요?”는 JavaScript 면접에서 자주 나오는 질문입니다.
먼저, 기초부터 이해하기
선언(Declaration)이란?
선언은 JavaScript 엔진에게 “이런 이름의 변수가 존재한다”고 알려주는 것입니다. 변수의 이름과 스코프를 정의하지만, 아직 값은 할당하지 않습니다.
var x; // x를 선언 (값은 없음)
let y; // y를 선언 (값은 없음)
초기화(Initialization)란?
초기화는 선언된 변수에 처음으로 값을 할당하는 것입니다.
var x = 5; // x를 선언하고 5로 초기화
let y = 10; // y를 선언하고 10으로 초기화
선언과 초기화의 차이점 시각화
┌─────────────────────────────────────────────┐
│ 선언 (Declaration) │
├─────────────────────────────────────────────┤
│ var x; │
│ ↓ │
│ JavaScript 엔진: "x라는 변수가 존재해" │
│ 메모리: [x: ?] │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ 초기화 (Initialization) │
├─────────────────────────────────────────────┤
│ x = 5; │
│ ↓ │
│ JavaScript 엔진: "x에 5를 저장해" │
│ 메모리: [x: 5] │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ 선언 + 초기화 (한 번에) │
├─────────────────────────────────────────────┤
│ var x = 5; │
│ ↓ │
│ JavaScript 엔진: "x를 만들고 5를 저장해" │
│ 메모리: [x: 5] │
└─────────────────────────────────────────────┘
1. var의 선언과 초기화
1-1. 기본 동작
MDN 공식 문서에 따르면, var로 선언된 변수는 선언만 호이스팅되고, 초기화는 호이스팅되지 않습니다.
console.log(x); // undefined (선언은 됐지만 초기화 안 됨)
var x = 5;
console.log(x); // 5 (이제 초기화됨)
엔진이 실제로 실행하는 방식:
var x; // 선언이 맨 위로 (호이스팅)
console.log(x); // undefined
x = 5; // 초기화는 원래 위치에서
console.log(x); // 5
1-2. 기본값은 undefined
var로 선언했지만 초기화하지 않은 변수의 기본값은 undefined입니다.
var x;
console.log(x); // undefined
var y = 10;
console.log(y); // 10
1-3. 여러 변수 동시 선언
// ✅ 선언만
var a, b, c;
console.log(a, b, c); // undefined undefined undefined
// ✅ 선언 + 초기화
var x = 1, y = 2, z = 3;
console.log(x, y, z); // 1 2 3
// ✅ 혼합
var name = "John", age, city = "Seoul";
console.log(name, age, city); // "John" undefined "Seoul"
1-4. 함수 스코프
var는 함수 스코프를 가집니다. 블록 스코프가 아닙니다!
function test() {
var x = 10; // 함수 내부에서 선언
if (true) {
var x = 20; // 같은 x를 재선언 (블록 스코프 아님!)
console.log(x); // 20
}
console.log(x); // 20 (if 블록 밖에서도 변경됨)
}
test();
2. let의 선언과 초기화
2-1. 기본 동작
let은 블록 스코프를 가지며, 선언은 호이스팅되지만 초기화 전에 접근하면 ReferenceError가 발생합니다.
// ❌ TDZ (Temporal Dead Zone)
console.log(x); // ReferenceError: Cannot access 'x' before initialization
let x = 5;
// ✅ 올바른 사용
let x = 5;
console.log(x); // 5
2-2. 기본값은 undefined
초기화하지 않은 let 변수의 기본값도 undefined입니다.
let x;
console.log(x); // undefined
x = 10;
console.log(x); // 10
2-3. 블록 스코프
let은 {}로 둘러싸인 블록 내부에서만 유효합니다.
{
let x = 10;
console.log(x); // 10
}
console.log(x); // ReferenceError: x is not defined
var와 let의 스코프 차이:
// var: 블록 스코프 무시
if (true) {
var x = 5;
}
console.log(x); // 5 (블록 밖에서도 접근 가능)
// let: 블록 스코프 존중
if (true) {
let y = 10;
}
console.log(y); // ReferenceError: y is not defined
2-4. 반복문에서의 차이
// ❌ var: 모든 타이머가 같은 i 참조
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 3, 3, 3
}
// ✅ let: 각 반복마다 새로운 i
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // 0, 1, 2
}
왜 이런 차이가 발생할까요?
var는 함수 스코프이므로 반복문이 끝난 후 i는 3입니다. 모든 setTimeout 콜백은 같은 i를 참조합니다.
let은 블록 스코프이므로 각 반복마다 새로운 i가 생성됩니다. 각 setTimeout 콜백은 자신만의 i를 가집니다.
3. const의 선언과 초기화
3-1. 반드시 초기화 필요
MDN 공식 문서에 따르면, const는 선언과 동시에 초기화해야 합니다.
// ❌ 초기화 없이 선언 불가
const x; // SyntaxError: Missing initializer in const declaration
// ✅ 선언 + 초기화
const x = 5;
console.log(x); // 5
3-2. 재할당 불가
const로 선언된 변수는 재할당할 수 없습니다.
const x = 5;
x = 10; // TypeError: Assignment to constant variable.
3-3. 블록 스코프
const도 let처럼 블록 스코프를 가집니다.
{
const x = 10;
console.log(x); // 10
}
console.log(x); // ReferenceError: x is not defined
3-4. 객체는 변경 가능
const는 변수에 할당된 참조를 변경할 수 없다는 의미입니다. 객체의 프로퍼티는 변경 가능합니다.
// ✅ 객체 프로퍼티 변경 가능
const person = { name: "John" };
person.name = "Jane"; // 가능!
console.log(person.name); // "Jane"
// ❌ 객체 자체 재할당 불가
person = { name: "Bob" }; // TypeError: Assignment to constant variable.
배열도 마찬가지:
const arr = [1, 2, 3];
// ✅ 배열 요소 변경 가능
arr.push(4);
console.log(arr); // [1, 2, 3, 4]
// ✅ 배열 요소 수정 가능
arr[0] = 100;
console.log(arr); // [100, 2, 3, 4]
// ❌ 배열 자체 재할당 불가
arr = [5, 6, 7]; // TypeError: Assignment to constant variable.
4. 비교표: var vs let vs const
| 특징 | var | let | const |
|---|---|---|---|
| 스코프 | 함수 스코프 | 블록 스코프 | 블록 스코프 |
| 호이스팅 | 선언만 호이스팅 | 선언만 호이스팅 | 선언만 호이스팅 |
| 초기화 전 접근 | undefined |
ReferenceError |
ReferenceError |
| 기본값 | undefined |
undefined |
없음 (필수) |
| 선언 시 초기화 | 선택 사항 | 선택 사항 | 필수 |
| 재선언 | 가능 | 불가 | 불가 |
| 재할당 | 가능 | 가능 | 불가 |
| TDZ | 없음 | 있음 | 있음 |
5. 실전 예제
예제 1: 선언과 초기화 분리
// var: 선언과 초기화 분리 가능
var x;
console.log(x); // undefined
x = 10;
console.log(x); // 10
// let: 선언과 초기화 분리 가능
let y;
console.log(y); // undefined
y = 20;
console.log(y); // 20
// const: 선언과 초기화 분리 불가
const z; // SyntaxError
예제 2: 조건부 초기화
// ✅ let 사용
let message;
if (isLoggedIn) {
message = "Welcome back!";
} else {
message = "Please log in.";
}
console.log(message);
// ❌ const 사용 불가 (선언과 초기화 분리 안 됨)
const greeting; // SyntaxError
if (isLoggedIn) {
greeting = "Welcome back!";
} else {
greeting = "Please log in.";
}
const로 조건부 초기화하려면:
// ✅ 삼항 연산자 사용
const message = isLoggedIn ? "Welcome back!" : "Please log in.";
console.log(message);
예제 3: 반복문 카운터
// ❌ var 사용 - 블록 스코프 무시
for (var i = 0; i < 3; i++) {
// ...
}
console.log(i); // 3 (반복문 밖에서도 접근 가능!)
// ✅ let 사용 - 블록 스코프
for (let j = 0; j < 3; j++) {
// ...
}
console.log(j); // ReferenceError: j is not defined
예제 4: 설정값 정의
// ✅ const 사용 - 변경하면 안 되는 값
const API_URL = "https://api.example.com";
const MAX_RETRIES = 3;
const TIMEOUT = 5000;
// API_URL = "https://new-api.example.com"; // TypeError!
예제 5: 함수 내부 변수
function calculateTotal(items) {
// ✅ let: 재할당이 필요한 변수
let total = 0;
// ✅ const: 변경하지 않을 변수
const tax = 0.1;
for (const item of items) { // const: 각 반복마다 새로운 item
total += item.price;
}
return total * (1 + tax);
}
6. 흔한 실수와 해결법
실수 1: const로 선언한 변수 재할당 시도
// ❌ 잘못된 예
const age = 25;
age = 26; // TypeError: Assignment to constant variable.
// ✅ 올바른 예 1: let 사용
let age = 25;
age = 26; // 가능!
// ✅ 올바른 예 2: 새 변수 선언
const initialAge = 25;
const currentAge = 26;
실수 2: const 없이 선언
// ❌ 잘못된 예
const name; // SyntaxError: Missing initializer in const declaration
name = "John";
// ✅ 올바른 예
const name = "John";
실수 3: var를 블록 스코프처럼 사용
// ❌ 잘못된 예
function getUser() {
if (true) {
var user = "John"; // 함수 스코프!
}
console.log(user); // "John" (if 블록 밖에서도 접근 가능)
}
// ✅ 올바른 예
function getUser() {
if (true) {
let user = "John"; // 블록 스코프
}
console.log(user); // ReferenceError: user is not defined
}
실수 4: 초기화 전에 사용
// ❌ 잘못된 예
console.log(name); // ReferenceError: Cannot access 'name' before initialization
let name = "John";
// ✅ 올바른 예
let name = "John";
console.log(name); // "John"
실수 5: const로 객체를 “불변”으로 만들려는 시도
// ❌ 오해: const로 선언하면 객체가 불변?
const person = { name: "John" };
person.name = "Jane"; // 가능! const는 참조만 고정
console.log(person.name); // "Jane"
// ✅ 진짜 불변 객체 만들기
const person = Object.freeze({ name: "John" });
person.name = "Jane"; // 무시됨 (strict mode에서는 에러)
console.log(person.name); // "John"
7. 디버깅 팁
팁 1: 변수가 선언됐는지 확인
// ✅ typeof 사용 (선언되지 않은 변수도 안전하게 체크)
if (typeof myVar !== 'undefined') {
console.log(myVar);
} else {
console.log("myVar가 선언되지 않았거나 undefined입니다.");
}
팁 2: 엄격 모드 사용
'use strict';
x = 5; // ReferenceError: x is not defined
// 선언 없이 변수를 사용하면 에러 발생
팁 3: ESLint 규칙 설정
.eslintrc.json:
{
"rules": {
"no-var": "error", // var 사용 금지
"prefer-const": "warn", // 재할당이 없으면 const 권장
"no-undef": "error", // 선언되지 않은 변수 사용 금지
"init-declarations": ["error", "always"] // 선언 시 초기화 강제
}
}
8. 베스트 프랙티스
1. 기본적으로 const 사용
// ✅ 권장: 재할당이 필요 없으면 const
const name = "John";
const age = 25;
const isAdmin = true;
2. 재할당이 필요하면 let 사용
// ✅ 권장: 재할당이 필요하면 let
let count = 0;
count++; // 가능
let status = "pending";
status = "completed"; // 가능
3. var는 사용하지 않기
// ❌ 피해야 할 방식
var x = 5;
// ✅ 권장하는 방식
const x = 5; // 또는
let x = 5;
4. 변수는 사용하기 직전에 선언
// ❌ 좋지 않은 방식
function processData(data) {
let result;
let temp;
let index;
// ... 많은 코드 ...
result = doSomething(data);
return result;
}
// ✅ 좋은 방식
function processData(data) {
// ... 다른 코드 ...
const result = doSomething(data);
return result;
}
5. 선언과 초기화를 함께
// ❌ 좋지 않은 방식
let name;
name = "John";
// ✅ 좋은 방식
let name = "John";
9. 면접 질문 대비
Q1: 선언과 초기화의 차이는 무엇인가요?
답변:
- 선언(Declaration)은 JavaScript 엔진에게 변수의 존재를 알리는 것입니다. 변수의 이름과 스코프를 정의합니다.
- 초기화(Initialization)는 선언된 변수에 처음으로 값을 할당하는 것입니다.
예를 들어, let x;는 선언이고, x = 5;는 초기화입니다. let x = 5;는 선언과 초기화를 동시에 하는 것입니다.
Q2: var, let, const의 초기화 동작 차이는?
답변:
var: 선언 시 초기화는 선택 사항입니다. 초기화하지 않으면undefined가 기본값입니다.let: 선언 시 초기화는 선택 사항입니다. 초기화하지 않으면undefined가 기본값입니다.const: 선언 시 반드시 초기화해야 합니다. 초기화하지 않으면SyntaxError가 발생합니다.
Q3: 다음 코드의 출력은?
console.log(a); // ?
var a = 1;
console.log(b); // ?
let b = 2;
const c; // ?
c = 3;
답변:
- 첫 번째
console.log(a):undefined(var는 선언이 호이스팅되고 기본값 undefined) - 두 번째
console.log(b):ReferenceError(let은 TDZ에 걸림) const c;:SyntaxError(const는 선언 시 초기화 필수)
Q4: const로 선언한 객체를 변경할 수 있나요?
답변: 네, 가능합니다. const는 변수에 할당된 참조를 변경할 수 없다는 의미입니다. 객체의 프로퍼티는 변경할 수 있습니다.
const person = { name: "John" };
person.name = "Jane"; // 가능 (프로퍼티 변경)
person = { name: "Bob" }; // 불가 (참조 변경)
Q5: 언제 let을 사용하고 언제 const를 사용해야 하나요?
답변:
- const: 재할당이 필요 없는 모든 경우 (기본 선택)
- let: 재할당이 필요한 경우만 (카운터, 누적값, 상태 변경 등)
// const 사용
const API_URL = "https://api.example.com";
const user = { name: "John" };
// let 사용
let count = 0;
count++; // 재할당 필요
let status = "pending";
status = "completed"; // 재할당 필요
10. 시각적 정리
선언 vs 초기화 타임라인
┌──────────────────────────────────────────────┐
│ var x = 5; │
└──────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────┐
│ 1단계: 선언 (호이스팅) │
│ var x; (메모리에 x 공간 확보) │
│ 값: undefined │
└──────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────┐
│ 2단계: 초기화 (원래 위치에서) │
│ x = 5; (x에 5 할당) │
│ 값: 5 │
└──────────────────────────────────────────────┘
var, let, const 생명주기
┌─────────────────────────────────────────────┐
│ var │
├─────────────────────────────────────────────┤
│ 선언 (호이스팅) → undefined → 초기화 │
│ ✓ 선언 전 접근 가능 (undefined 반환) │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ let │
├─────────────────────────────────────────────┤
│ 선언 (호이스팅) → TDZ → 초기화 │
│ ✗ 선언 전 접근 불가 (ReferenceError) │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ const │
├─────────────────────────────────────────────┤
│ 선언 (호이스팅) → TDZ → 선언+초기화 동시 │
│ ✗ 선언 전 접근 불가 (ReferenceError) │
│ ✗ 초기화 없이 선언 불가 (SyntaxError) │
└─────────────────────────────────────────────┘
마치며
축하합니다! 이제 JavaScript 변수의 선언과 초기화의 모든 것을 배웠습니다.
핵심 정리:
- 선언: 변수의 존재를 알리는 것
- 초기화: 변수에 처음으로 값을 할당하는 것
- var: 선언만 호이스팅, 기본값
undefined, 함수 스코프 - let: 선언만 호이스팅 + TDZ, 기본값
undefined, 블록 스코프 - const: 선언만 호이스팅 + TDZ, 초기화 필수, 블록 스코프, 재할당 불가
다음 단계:
- 호이스팅과 선언/초기화의 관계 이해하기
- 스코프와 클로저 공부하기
- 실행 컨텍스트와 렉시컬 환경 탐구하기
- 코드베이스에서
var를let/const로 리팩토링하기
유용한 리소스:
실습 과제:
var,let,const의 차이를 실험하는 코드 작성하기- 선언과 초기화의 차이를 보여주는 예제 만들기
- TDZ를 직접 재현하고 이해하기
- 레거시 코드의
var를let/const로 변환하기
변수의 선언과 초기화를 이해하면 JavaScript의 동작 원리를 더 깊이 알게 되고, 예측 가능한 코드를 작성할 수 있습니다. 이제 자신있게 let과 const를 사용하고, var의 함정을 피하세요!
댓글