Cannot access before initialization 오류(TDZ) 해결하기
자바스크립트를 실행하다가 ReferenceError: Cannot access '변수명' before initialization이라는 오류를 만날 때가 있습니다. 분명 아래쪽에 변수를 선언해 두었는데 왜 “초기화 전에 접근했다”고 나오는지 처음에는 이해하기 어려운데요.
Cannot access before initialization 오류를 이해하려면 let과 const, 그리고 TDZ(Temporal Dead Zone) 개념을 알아두는 것이 중요합니다. 단순히 변수 선언을 위로 옮우는 방법뿐 아니라 왜 이런 오류가 발생하는지 원리부터 차근차근 살펴보겠습니다.
Cannot access before initialization 뜻은?
에러 메시지를 그대로 풀어보면 “변수가 초기화되기 전에 해당 변수에 접근할 수 없다”는 의미입니다. 대표적인 예시는 다음과 같습니다.
console.log(userName);
let userName = "민수";
userName은 코드 아래쪽에서 선언되어 있지만 console.log()가 먼저 실행됩니다. 이 시점에는 해당 변수를 사용할 수 없기 때문에 ReferenceError가 발생합니다.
해결 자체는 간단합니다. 변수를 초기화한 다음 사용하면 됩니다.
let userName = "민수";
console.log(userName);
TDZ란? Temporal Dead Zone 쉽게 이해하기
TDZ는 Temporal Dead Zone의 약자로, 우리말로는 흔히 ‘일시적 사각지대’ 정도로 설명합니다. let, const 등 특정 선언이 속한 스코프에 진입한 시점부터 실제 선언·초기화가 실행되기 전까지 해당 식별자에 접근할 수 없는 구간을 말합니다.
{
// user에 접근할 수 없는 TDZ 구간
const user = "민수";
// 여기부터 user 사용 가능
console.log(user);
}
TDZ는 단순히 “변수가 존재하지 않는다”는 의미와는 조금 다릅니다. 해당 스코프의 변수로 결정되어 있지만 초기화되기 전이므로 접근이 허용되지 않는 상태라고 이해하면 쉽습니다.
호이스팅됐는데 왜 let을 사용할 수 없을까?
자바스크립트의 호이스팅을 배우면서 “변수 선언은 위로 올라간다”고 기억하는 경우가 많습니다. 하지만 이를 실제 코드가 물리적으로 위쪽으로 이동한다고 이해하면 TDZ가 더욱 헷갈릴 수 있습니다.
let과 const 선언 역시 스코프 처리 과정과 관련이 있지만, 선언 코드가 실행되기 전에는 TDZ에 있기 때문에 값을 읽을 수 없습니다.
console.log(score);
let score = 100;
따라서 “호이스팅된다 = 선언 전에 자유롭게 사용할 수 있다”는 의미가 아닙니다. let과 const는 실제 선언 위치에 도달하기 전에 접근하면 ReferenceError가 발생한다는 점을 함께 기억해야 합니다.
var와 let의 차이를 비교하면 쉬워요
var와 비교하면 Cannot access before initialization 오류를 이해하기가 한결 쉬워집니다.
console.log(a);
var a = 10;
// undefined
var는 선언 처리 과정에서 undefined로 초기화되기 때문에 선언문보다 앞에서 읽으면 일반적으로 undefined가 나타납니다. 반면 let은 선언 코드에 도달하기 전 TDZ에 있으므로 접근할 수 없습니다.
console.log(b);
let b = 10;
// ReferenceError
그렇다고 오류를 피하기 위해 let을 모두 var로 변경할 필요는 없습니다. 오히려 let과 const가 초기화 전 접근을 오류로 알려주기 때문에 코드의 잘못된 실행 순서를 발견하는 데 도움이 됩니다.
const에서도 Cannot access 오류가 발생할까?
네. const 역시 초기화되기 전에 접근하면 같은 종류의 오류가 발생할 수 있습니다.
console.log(API_KEY);
const API_KEY = "sample-key";
const는 여기에 한 가지 규칙이 더 있습니다. 선언과 동시에 초기값을 지정해야 합니다.
const count = 0; // 정상
const count;처럼 초기값 없이 선언하면 TDZ 문제가 아니라 앞서 살펴본 Missing initializer in const declaration과 같은 별도의 SyntaxError가 발생합니다. 두 오류를 구분해두면 디버깅이 훨씬 쉬워집니다.
블록 스코프 때문에 TDZ 오류가 생기는 경우
바깥쪽에 같은 이름의 변수가 이미 있다고 해서 항상 그 변수를 사용할 수 있는 것은 아닙니다. 블록 내부에 같은 이름의 let 또는 const 선언이 있다면 해당 블록에서는 안쪽 변수가 기준이 됩니다.
const message = "바깥쪽";
{
console.log(message);
const message = "안쪽";
}
처음 보면 "바깥쪽"이 출력될 것 같지만 그렇지 않습니다. 블록 안의 message가 별도의 식별자로 존재하며, 선언문에 도달하기 전에는 TDZ에 있기 때문에 오류가 발생합니다.
이처럼 변수 선언이 아래쪽에 숨어 있는 경우는 TDZ 오류를 찾기 어렵게 만드는 대표적인 패턴입니다.
함수와 변수 실행 순서도 확인하세요
함수가 선언된 위치만 보고 판단하면 원인을 놓칠 수도 있습니다. 중요한 것은 함수가 실제로 호출되는 순간 필요한 변수가 초기화되어 있는가입니다.
function printUser() {
console.log(user);
}
printUser();
const user = "민수";
printUser() 내부에서는 user를 사용하고 있으며, 함수가 호출되는 시점에는 아직 const 초기화가 완료되지 않았습니다. 다음처럼 변수 초기화 이후 함수를 호출하면 문제가 해결됩니다.
function printUser() {
console.log(user);
}
const user = "민수";
printUser();
클래스도 초기화 전에 접근하면 주의!
TDZ는 let과 const만 외우고 끝내기보다 class 선언에서도 초기화 전 접근에 주의해야 합니다.
const user = new User();
class User {
constructor() {
this.name = "민수";
}
}
클래스 선언이 실행되기 전에 User에 접근하면 오류가 발생할 수 있습니다. 클래스 역시 실제 선언이 평가된 이후 사용하는 방식으로 코드를 배치하는 것이 안전합니다.
모듈의 순환 의존성도 확인하세요
코드 위치가 정상인데 ES Module을 사용한 여러 파일 사이에서 이 오류가 나타난다면 순환 의존성(circular dependency)도 확인할 필요가 있습니다. 예를 들어 A 모듈이 B를 import하고, B가 다시 A의 값을 import하는 구조입니다.
이 경우 모듈 초기화 순서 때문에 아직 초기화되지 않은 값을 다른 모듈에서 먼저 읽으려는 상황이 생길 수 있습니다. 단순히 한 줄을 위로 옮기는 것으로 해결되지 않는다면 import 관계를 확인하고 공통 기능을 별도 모듈로 분리하는 등 구조를 정리해 보세요.
Cannot access before initialization 주요 원인
1. let 변수를 선언하기 전에 사용한 경우
2. const 변수를 초기화하기 전에 접근한 경우
3. 블록 안에서 같은 이름의 변수가 다시 선언된 경우
4. 함수가 변수 초기화보다 먼저 호출된 경우
5. class 선언보다 먼저 클래스를 사용한 경우
6. ES Module 사이에 순환 의존성이 생긴 경우
7. 리팩터링하면서 변수 선언과 실행 순서가 뒤바뀐 경우
공통점은 단순합니다. 필요한 값의 초기화가 끝나기 전에 코드가 해당 식별자에 접근하고 있다는 것입니다. 따라서 오류 줄뿐 아니라 변수의 선언 위치와 실제 실행 순서를 함께 확인해야 합니다.
TDZ 오류 해결 순서 7단계
먼저 오류 메시지에서 Cannot access '이름' before initialization에 표시되는 식별자를 확인하세요. 그런 다음 프로젝트에서 해당 이름의 선언 위치와 실제 사용 위치를 찾아 비교합니다.
| 순서 | 확인할 내용 |
| 1단계 | 오류 메시지의 변수명 확인 |
| 2단계 | let·const·class 선언 위치 검색 |
| 3단계 | 선언보다 먼저 접근하는 코드 확인 |
| 4단계 | 같은 이름의 블록 변수 존재 여부 확인 |
| 5단계 | 함수의 실제 호출 순서 확인 |
| 6단계 | 모듈 간 순환 import 여부 확인 |
| 7단계 | 초기화 이후 사용하도록 코드 구조 수정 |
TDZ 오류를 예방하는 방법
변수는 가능하면 사용하기 전에 선언하고 초기화하는 습관을 들이는 것이 좋습니다. 같은 이름의 변수를 여러 스코프에서 반복해서 사용하면 코드 흐름을 착각하기 쉬우므로 의미가 명확한 변수명을 사용하는 것도 도움이 됩니다.
또한 오류가 발생했다고 단순히 let을 var로 변경하는 방식은 피하는 것이 좋습니다. 에러가 사라져도 원래 존재했던 잘못된 실행 순서가 그대로 남을 수 있기 때문입니다.
Cannot access before initialization은 결국 변수가 초기화되기 전에 접근했다는 ReferenceError입니다. let과 const의 선언 위치를 먼저 확인하고, 문제가 보이지 않는다면 블록 스코프, 함수 호출 순서, 클래스와 모듈의 순환 의존성까지 차례대로 살펴보세요.
TDZ를 어렵게 외울 필요는 없습니다. “let·const는 선언 코드에 도달하기 전까지 함부로 사용할 수 없다”는 원칙부터 기억하면 비슷한 오류를 훨씬 쉽게 이해하고 해결할 수 있습니다.