자바스크립트 호이스팅 톺아보기
v1 개정 2025.04.20
2025.02.02
· Updated 2025.04.20

산업용 호이스트

위 그림은 산업 또는 건설 현장에서 사용되는 호이스트(Hoist)입니다. 주로 엄청나게 무거운 사물을 들어올릴 때 사용되죠. 또한, hoist라는 단어는 ‘끌어올리다’라는 의미를 가지고 있습니다.
자바스크립트만의 독특한 특징 중 하나로 호이스팅(Hoisting)을 꼽을 수 있습니다.
호이스팅이란 변수나 함수 선언문이 코드의 상단으로 끌어 올려진 것처럼 동작하는 현상을 말합니다.
호이스팅의 감을 잡기 위해, 일반적인 언어와 자바스크립트에서 동일한 코드를 실행했을 때의 차이를 먼저 살펴보겠습니다.
일반적인 언어에서는 선언하지 않은 변수를 사용하면 즉시 오류가 발생합니다.
예를 들어, 다음과 같은 자바 코드를 생각해봅시다.
public class Main {
    public static void main(String[] args) {
        System.out.println(variable); // ❌ 컴파일 에러 발생
        System.out.println(variable2);

        int variable = 30; // 변수 선언
    }
}
Java
자바 컴파일러는 코드를 위에서 아래로 읽으면서 파싱합니다.
variable 을 출력하는 시점에 아직 선언이 되어 있지 않다는 이유로 컴파일 에러가 발생합니다.
이번에는 똑같은 코드를 크롬 개발자 도구(F12) 콘솔에 입력해보면 결과가 다릅니다.

자바스크립트에서 variable, variable2를 초기화 전에 출력하기

결과를 보면
  • variable → undefined
  • variable2 → “Uncaught ReferenceError: variable2 is not defined”
  • 무슨 차이일까요?
    variable 은 아래에서라도 선언만 되어 있었다면, 위에서 접근해도 에러 없이 undefined가 출력됩니다. 반면 variable2 는 선언 자체가 없기 때문에 Reference Error가 발생합니다.
    이것이 바로 자바스크립트의 호이스팅입니다. 선언문이 호출보다 아래에 있을 때, 선언이 끌어올려진 것처럼 동작하는 신기한 현상이죠.
    이 이상한 현상은 사실, 자바스크립트 엔진의 코드 실행 방식에 의해 당연히 발생하는 것입니다. 호이스팅을 제대로 이해하려면, 우선 자바스크립트 엔진이 내부적으로 코드를 어떻게 처리하는지부터 알아야 합니다.
    이번 글에서는 V8엔진을 기준으로, 그 동작 과정을 하나하나 짚어보겠습니다.
    자바스크립트 엔진을 이해하기 전에, 브라우저(Brower) 전체 관점에서 어떤 일들이 일어나는지 먼저 살펴볼게요.
    브라우저는 서버로부터 HTML, CSS, JavaScript 파일을 다운로드한 다음, Blink (HTML 파서 엔진) 같은 파서를 이용해 이 파일들을 파싱하고, 최종적으로 화면에 렌더링합니다.

    네이버 접속 후 개발자 도구(F12) 내 네트워크 정보

    예를 들어 크롬 브라우저를 통해 네이버에 접속하면 위 그림과 같이 네이버 서버로부터 HTML파일과 js파일을 다운로드 받고 렌더링해서 화면에 그려줍니다. 클라이언트 사이드 렌더링(CSR)의 대표적인 사례입니다.
    브라우저의 렌더링 파이프라인은 다음과 같이 요약할 수 있습니다.
    HTML 다운로드
    ↓
    Blink HTML 파서
    ↓
    DOM Tree 생성
    ↓
    Render Tree 생성
    ↓
    CSS, 픽셀화 작업 등 수행
    ↓
    렌더링 (화면 출력)
    Plain
    Blink가 HTML 태그들을 읽어가면서 <script> 태그를 만나면 내부적으로 ProcessScriptStartTag(token) 함수를 호출해 스크립트 엘리먼트를 DOM에 추가하고 </script> 태그가 나타날 때까지 내용을 읽어갑니다.
    그 후, script_to_process_ 스마트 포인터에 스크립트를 등록해두고, 파서가 멈추거나 플러시하는 시점에 자바스크립트 엔진이 스크립트를 실행합니다.
    이 시점이 바로 자바스크립트 코드가 실제로 실행되기 시작하는 순간입니다.
    🎈참고: <script>를 <body> 최하단에 넣어야 하는 이유도 바로 이것입니다. HTML 파싱을 방해하지 않고, 문서가 다 파싱된 후 스크립트를 실행할 수 있기 때문이죠. 참고로 HTML 중간에 <script>가 있을 경우, 이를 실행하는 동안 HTML파싱이 멈추어 렌더링이 지연되는 현상을 파서 블로킹(Parser Blocking) 현상이라고 합니다
    앞서 호이스팅이 자바스크립트 엔진의 실행 방식에 따라 발생한다고 했습니다. 그렇다면 구체적으로 자바스크립트 엔진은 자바스크립트 코드를 어떤 순서로 해석하고 실행할까요?
    V8 엔진을 기준으로 보면 다음과 같은 4단계로 이루어집니다.
    자바스크립트 파일이 로드되면, 엔진은 먼저 해당 소스를 텍스트에서 의미 있는 단위(Token)으로 나누는 작업인 토크나이징(Tokenizing)을 수행합니다. 그다음 이 토큰들을 기반으로 문장의 구조를 이해하는 구문 분석(Parsing) 단계를 거칩니다. 이 과정을 통해 엔진은 AST(Abstract Syntax Tree), 즉 추상 구문 트리를 생성합니다.
    이 트리는 자바스크립트 코드의 의미적인 구조를 담고 있는 핵심 자료 구조입니다. AST Explorer라는 사이트에서 Tree 혹은 JSON 형태로 결과물을 확인해볼 수 있습니다.
    🎈참고: 이 단계에서 엔진은 변수와 함수의 선언문을 모두 미리 인식해 메모리에 올려놓습니다. 실행 단계(런타임)가 시작되기 전부터 어떤 선언들이 존재하는지 이미 기억하고 있는 상태가 되는거죠.
    AST는 자바스크립트 코드를 어떤 “의미 단위”로 나누어 구조화한 트리입니다. 이 구조는 단순한 코드의 나열이 아닌, 엔진이 의미를 해석하기 위한 자료 구조의 형태입니다. 예를 들어 다음과 같은 코드가 있다고 해봅시다.
    var x = 10;
    Javascript
    이 코드는 AST 구조 상 다음과 같이 나뉘게 됩니다.
    Declaration: var x
    Assignment: x = 10
    Makefile
    즉, 선언(Declaration)과 초기화(Assignment)가 완전히 분리됩니다. 이 때문에 var 변수는 선언만 끌어올려지고, 초기화는 실행 시점까지 미뤄져서 undefined가 출력되는 것이죠.
    AST가 생성된 후, V8 엔진은 이를 바탕으로 자체 인터프리터인 Ignition이 실행할 수 있는 바이트코드(Bytecode)로 변환합니다. 바이트코드는 엔진 입장에서 가볍고 빠르게 해석 가능한 중간 언어입니다.
    이 단계에서 엔진은 변수, 스코프, 클로저 환경까지 함께 설정합니다. 즉, 실행을 위한 모든 준비가 끝나는 단계입니다.
    바이트코드가 준비되면, Ignition 인터프리터가 이를 한 줄씩 해석하면서 실행합니다. 하지만 여기서 끝이 아닙니다. 실행 도중 특정 코드가 반복적으로 실행된다면, Turbofan이라는 JIT(Just-In-Time) 컴파일러가 개입해 해당 코드를 기계어(Machine Code)로 변환합니다.
    이렇게 되면 처음엔 느리게 해석되던 코드가 점점 더 빠르게 실행되면 성능이 향상됩니다. 인터프리팅에서 시작해 최적화된 기계어로 점차 진화하는 방식이 V8 엔진이 빠른 이유 중 하나입니다.
    이 모든 과정을 보면 알 수 있습니다. 호이스팅은 단순한 문법적인 트릭이 아니라 자바스크립트 엔진이 소스를 파싱하고, 선언을 미리 메모리에 등록하는 과정에서 자연스럽게 발생하는 현상입니다. 즉, 호이스팅은 “코드를 실행할 때 끌어올리는 것”이 아니라 “실행 전에 미리 기억해 두는 것”에 가깝습니다. 엔진을 이해하면 호이스팅은 전혀 이상한 일이 아니며, 오히려 합리적인 동작입니다.
    자바스크립트에서는 var, let, const 모두 선언이 호이스팅됩니다. 하지만 이 세 키워드 사이에는 호이스팅 이후 초기 상태와 접근 가능성에서 중요한 차이가 존재합니다.
    우선 var 키워드로 선언된 변수는 호이스팅될 때 선언만 끌어올려지는 것이 아니라, 동시에 undefined라는 값으로 초기화됩니다. 덕분에 변수를 선언하기 전에 접근해도 프로그램이 중단되지 않고 undefined를 반환합니다. 따라서 var로 선언된 변수는 코드의 최상단부터 접근이 가능하다고 볼 수 있습니다.
    반면 letconst 키워드는 조금 다르게 작동합니다. 두 키워드 역시 선언은 호이스팅되지만, 끌어올려진 직후 바로 초기화되지는 않습니다. 이들은 메모리에 등록되지만, 초기화되기 전까지는 TDZ(Temporal Dead Zone) 라는 특별한 상태에 놓입니다. TDZ에 있는 동안, 해당 변수에 접근하려고 시도하면 자바스크립트 엔진은 ReferenceError를 발생시키며 실행을 중단합니다.
    즉, let이나 const로 선언된 변수는 반드시 선언 이후에만 정상적으로 접근할 수 있습니다.
    요약하면, var는 선언과 동시에 undefined로 초기화되기 때문에 어디서든 접근이 가능하지만, letconst는 선언 직후에는 초기화되지 않고 TDZ에 있기 때문에 초기화되기 전에는 접근 자체가 불가능합니다. 이러한 차이 덕분에 letconst는 코드의 안정성과 예측 가능성을 높여줍니다. 특히 const는 선언 후 값을 변경할 수 없기 때문에, 변하지 않는 데이터를 다룰 때 유용하게 사용됩니다.
    자바스크립트에서는 var , let , const 로 선언된 변수 뿐만 아니라, 함수 선언문(function) 역시 호이스팅 대상입니다. 그러나 변수와 함수 선언 그리고 함수 표현식은 호이스팅 방식이 서로 다르게 동작합니다. 이를 정확히 이해하면 자바스크립트 코드의 실행 흐름을 훨씬 명확하게 파악할 수 있습니다.
    함수 선언문(function)은 변수와는 조금 다르게 호이스팅됩니다. 함수 선언문은 코드 실행 전에 선언 자체와 함수 객체 전체가 메모리에 등록됩니다. 덕분에 함수 선언문으로 정의된 함수는 코드 어디에서든 호출할 수 있습니다.
    예를 들어
    sayHello(); // "Hello"
    
    function sayHello() {
      console.log("Hello");
    }
    Javascript
    이 코드는 정상적으로 "Hello"를 출력합니다. 함수 전체가 위로 끌어올려졌기 때문입니다.
    반면, 함수 표현식은 동작 방식이 다릅니다. 함수 표현식은 변수(var, let, const)에 함수 객체를 할당하는 것이므로 변수의 호이스팅 규칙을 그대로 따릅니다.
    var로 선언된 함수 표현식은 변수 선언만 끌어올려지고, 초기화는 undefined로 이루어집니다. 따라서 함수 호출 시점이 변수 초기화보다 앞서면 TypeError가 발생합니다.
    sayHi(); // ❌ 타입 에러 발생
    
    var sayHi = function () {
      console.log("Hi");
    };
    Javascript
    sayHi라는 변수가 끌어올려지긴 하지만, 함수 객체는 아직 할당되지 않았기 때문에 문제가 발생하는 것입니다.
    let이나 const로 선언된 함수 표현식은 TDZ 규칙을 따르기 때문에, 초기화 전에 접근하면ReferenceError가 발생합니다.
    greet(); // ❌ 참조 에러 발생
    
    const greet = function () {
      console.log("Greetings");
    };
    Javascript
    지금까지 자바스크립트의 호이스팅을 엔진 내부 동작 원리부터 변수, 함수에 이르기까지 깊이 있게 살펴보았습니다. 호이스팅은 단순히 선언을 끌어올리는 현상이 아니라, 자바스크립트 엔진이 코드를 실행하기 전에 변수와 함수 선언을 미리 메모리에 등록하는 과정에서 자연스럽게 발생하는 동작입니다.
    특히, var, let, const처럼 변수 선언 방식에 따라 초기화 여부와 접근 가능성이 달라지고, 함수 선언문과 함수 표현식 또한 서로 다른 호이스팅 특성을 가진다는 사실을 알게 되었습니다.
    결국, 자바스크립트에서 호이스팅을 정확히 이해한다는 것은 단순히 문법을 아는 것을 넘어, 코드가 메모리에 어떻게 준비되고, 어떤 순서로 실행되는지를 이해하는 것입니다.
    감사합니다.

    Loading comments...

    관련 포스트

    © Churnobyl 성철민
    Contact: tjdcjfals@gmail.com