처음에는 단순히 “선언이 끌어올려진다”는 설명만 기억했는데, 실제로는 실행 컨텍스트의 생성 단계에서 메모리에 미리 등록되는 과정이라는 것을 이해하고 나니 호이스팅의 동작 원리가 훨씬 명확해졌습니다.
특히 var, let, const, 함수 선언문, 함수 표현식의 차이를 정리하면서, 앞으로 코드를 작성할 때 선언 위치와 TDZ를 더 주의 깊게 살펴야겠다고 느꼈습니다.
자바스크립트를 공부하다 보면 빠지지 않고 등장하는 키워드가 바로 실행 컨텍스트(Execution Context) 와 호이스팅(Hoisting) 입니다. 자바스크립트 코드가 실행되는 원리를 이해하는 데 핵심적인 개념이기 때문에 자세히 공부해보고 싶어 글을 정리하게 되었습니다.
실행 컨텍스트와 호이스팅이란?
실행 컨텍스트(Execution Context)는 자바스크립트 코드가 실행되는 환경 정보의 집합으로, 스코프, 변수, 함수 선언, this, 외부 참조 등이 포함됩니다. 코드가 실행되면 JS 엔진은 실행 컨텍스트를 생성하고, 이를 스택 구조(Call Stack) 로 관리합니다.
호이스팅(Hoisting)은 실행 컨텍스트 생성 단계에서 변수와 함수 선언이 먼저 메모리에 등록되기 때문에, 코드 상에서 마치 선언이 최상단으로 끌어올려진 것처럼 동작하는 현상입니다. 실제로는 끌어올려지는 것이 아니라, JS 엔진이 미리 인식하고 메모리에 등록하는 과정입니다.
실행 컨텍스트와 호이스팅이 문제를 일으키는 상황
호이스팅은 의도하지 않은 값 참조를 만들어 버그를 유발할 수 있습니다.
예를 들어 var 키워드로 선언된 변수는 선언만 먼저 끌어올려지고 undefined로 초기화되므로, 초기화 전에 접근해도 에러가 아니라 undefined를 반환합니다. 이는 로직 상 오류를 감추는 원인이 될 수 있습니다. 반대로 let, const는 TDZ(Temporal Dead Zone) 덕분에 초기화 전 접근 시 ReferenceError를 발생시켜 이런 문제를 방지합니다.
특징
1. 실행 컨텍스트 생성 과정
- 생성 단계: 변수와 함수 선언을 메모리에 등록
- 실행 단계: 코드가 실제 실행되며 값이 할당되고 함수가 호출
실행 컨텍스트의 생성/실행 단계에 대해 설명해주세요
실행 컨텍스트는 생성 단계와 실행 단계로 나뉩니다.
생성 단계에서는 실행 컨텍스트가 초기화되며, JS 엔진이 스코프 체인을 분석해 렉시컬 환경(LE) 과 변수 환경(VE) 을 구성하고, 모든 변수와 함수 선언을 메모리에 등록합니다. 이 시점에 var는 undefined로 초기화되고, let과 const는 TDZ(Temporal Dead Zone) 에 들어갑니다.
실행 단계에서는 생성 단계에서 준비된 환경을 바탕으로 실제 코드가 한 줄씩 실행됩니다. 이때 변수에 값이 할당되고, 함수가 호출되며, 새로운 실행 컨텍스트가 스택 위에 추가됩니다. 실행이 끝나면 해당 컨텍스트는 콜 스택에서 제거(pop) 됩니다.
2. 호이스팅의 방식
| 키워드 | TDZ(Temporal Dead Zone) | 선언 전 접근 | 특징 요약 |
| var | ❌ 없음 | undefined | 오래된 방식, 함수 스코프 |
| let/const | ✅ 있음 | ReferenceError | 블록 스코프, 안전한 선언 |
| function 선언문 | ❌ 없음 | ✅ 가능 | 코드 상단으로 완전히 끌어올려짐 |
| function 표현식 | 키워드 규칙 따름 | var → undefined let/const → ReferenceError |
함수지만 변수 취급 |
- var: 선언만 호이스팅, 초기화는 실행 단계
- let/const: 선언은 호이스팅되지만 TDZ로 초기화 전 접근 불가
- 함수 선언문: 선언과 정의가 함께 호이스팅 → 선언 전에 호출 가능
- 함수 표현식: 변수 규칙을 따름(var는 undefined, let/const는 ReferenceError)
3. 스택 구조
전역 컨텍스트 → 함수 실행 시 새로운 컨텍스트가 생성 → 종료 시 스택에서 제거
코드가 실행되면 전역 실행 컨텍스트(Global Execution Context) 가 가장 먼저 스택에 쌓이고, 함수가 호출될 때마다 새로운 함수 실행 컨텍스트(Function Execution Context) 가 스택의 맨 위에 추가됩니다. 함수 실행이 끝나면 해당 컨텍스트는 스택에서 제거(pop)되며, 제어권이 바로 아래 컨텍스트로 돌아갑니다.
사용 예시
1. 변수 호이스팅
console.log(a); // undefined
var a = 10;
console.log(b); // ReferenceError
let b = 20;
var a
- 실행 컨텍스트 생성 단계에서 var a는 선언만 먼저 메모리에 등록되고, undefined로 초기화됩니다.
- 따라서 선언 이전에 접근해도 에러가 아니라 undefined가 출력됩니다.
let b
- let은 선언 자체는 호이스팅되지만, 초기화가 실행 단계에서 이뤄지기 전까지는 TDZ(Temporal Dead Zone)에 묶여 있습니다.
- 따라서 초기화 전에 접근하면 ReferenceError가 발생합니다.
2. 함수 선언문 호이스팅
함수 선언문은 호이스팅 덕분에 코드 순서와 상관없이 호출 가능하지만, 과도하게 의존하면 가독성이 떨어질 수 있으므로 선언 위치를 명확히 두는 게 좋습니다.
greet(); // "Hello"
function greet() {
console.log("Hello");
}
- 함수 선언문은 선언과 정의(함수 본문 전체) 가 생성 단계에서 메모리에 등록됩니다.
- 따라서 greet 함수는 코드 어디서든 접근할 수 있고, 선언 전에 호출해도 정상적으로 실행됩니다.
3. 함수 표현식
sayHi(); // TypeError: sayHi is not a function
var sayHi = function() {
console.log("Hi");
};
- var sayHi는 변수 선언만 먼저 호이스팅되고, 초기값은 undefined로 세팅됩니다.
- 따라서 sayHi(); 실행 시점에는 sayHi가 아직 undefined 상태라서 함수 호출을 시도하다 TypeError가 발생합니다.
보조 개념 정리
- 스코프(Scope): 변수를 참조할 수 있는 유효 범위
- TDZ(Temporal Dead Zone): let/const로 선언된 변수가 초기화되기 전까지 접근할 수 없는 구간
- 콜 스택(Call Stack): 실행 컨텍스트가 쌓이고 제거되는 구조
더 공부해보고 싶은 내용
- 함수 실행 컨텍스트와 전역 실행 컨텍스트가 메모리 관점에서 어떻게 다른지?
- 호이스팅 과정에서 V8 엔진이 변수 환경과 렉시컬 환경을 실제로 어떻게 구분 관리하는지?
- 함수 표현식과 화살표 함수가 호이스팅 단계에서 어떻게 처리되는지?
'📚 CS > JavaScript' 카테고리의 다른 글
| [JavaScript] 프로토타입(Prototype)과 클래스(Class) (0) | 2025.09.03 |
|---|---|
| [JavaScript] 스코프 체인 (Scope Chain) (1) | 2025.08.31 |
| [JavaScript] this 바인딩 (0) | 2025.08.30 |
| [JavaScript] V8 엔진과 GC (Garbage Collection) (3) | 2025.08.28 |
| [JavaScript] 클로저(Closure)란? (1) | 2025.08.24 |