thumbnail
기술아티클
호이스팅 깊게 이해하기
자바스크립트에서의 호이스팅에 대해 설명합니다
December 30, 2025

들어가며

프론트엔드 개발자 면접 질문에 단골소재로 나오는 개념이기도 하며 자바스크립트에서 중요한 개념중 하나가 바로 호이스팅입니다. 그런데 더글러스 크록포드가 쓴 「자바스크립트: 세상에서 가장 오해가 많은 프로그래밍 언어」 라는 글처럼, 호이스팅에도 몇가지 오해가 있는것 같습니다. 이번 포스팅에서는 자바스크립트 호이스팅과 관련된 몇가지 오해를 풀어나가면서 호이스팅에 대해 깊이있게 이해해 봅니다.

호이스팅의 정의

호이스팅에 대해서 구글에 찾아보면 많은 설명이 있지만 그중에서 자바스크립트에서 호이스팅은 변수의 선언을 코드 최상단으로 끌어올리는것이다. 이때 함수선언문의 몸체도 최상단으로 끌어올려진다. 와 같은 설명이 있습니다.

그래서 이를 검증해보고자 아래와 같은 코드를 작성해서 실행해보면 선언이전에 참조한 a,b 를 사용할때 오류가 나오지않고, 선언이전에 sum함수를 사용했지만 아무문제 없이 실행이 되는것을 볼수 있습니다.

console.log(a, b) // undefind,undefind
var a = 1
var b = 2

sum(a, b) // 3

function sum(a, b) {
  return a + b
}

그렇다면 정말로 자바스크립트 엔진은 자바스크립트로 작성된 코드를 실행하기전에 아래와같은 형태로 변경하는걸까요? 사실은 그렇지 않습니다. 이에 대해서 이해하기 위해서는 실행컨텍스트에대한 이해가 필요합니다.

var a = undefined
var b = undefined

function sum(a, b) {
  return a + b
}

var a = 1
var b = 2

sum(a, b) // 3

실행컨텍스트 와 호이스팅

실행컨텍스트는 실행할 코드에 제공할 환경정보를 모아놓은 객체입니다. 쉽게 말하자면 코드가 실행될때 제공되는 정보들입니다. 여기에는 VariableEnvironment, LexicalEnvironment, ThisBinding 총 세가지 정보가 담기게 됩니다. ThisBinding 은 흔히 말하는 this 값이 담기며, VariableEnvironment 는 LexicalEnvironment와 아주 유사한데, 여기서는 실행컨텍스트가 생성될 당시의 LexicalEnvironment의 스냅샷이라고 생각하면 좋을것 같습니다.

그러면 분석이 필요한 부분은 LexicalEnvironment입니다. 이 LexicalEnvironment도 두 부분으로 나뉩니다. 현재 컨텍스트와 관련된 코드의 식별자정보가 저장되는 environmentRecord 와 스코프체인과 관련된 정보를 저장하는 outerEnvironmentReference입니다. 두번째 정보는 클로저, 스코프체인과 관련이 있기때문에 여기서 분석할 대상은 바로 첫번째 정보인 environmentRecord 입니다.

environmentRecord는 코드를 실행하기 이전에 코드의 식별자 즉 변수명을 먼저 수집합니다. 이렇게 수집하는 이유는 식별자의 중복등을 미리파악하고, 메모리에 변수를 할당하기 위함입니다. 따라서 자바스크립트 엔진은 코드를 실행하기전에 아래 코드에 대해서 식별자를 수집해 foo, bar이라는 식별자가 있다고 저장해두게됩니다.

여기서 호이스팅의 개념이 등장합니다. 자바스크립트엔진이 코드를 실행하기 이전에 식별자를 수집해서 먼저 알고 있다 보니 마치 식별자를 끌어올리는것과 동일해지는것입니다. 그래서 실제로 자바스크립트 엔진이 식별자를 끌어올리는것은 아니지만, 끌어올린것처럼 간주하자는게 바로 호이스팅입니다.

따라서 앞서 소개한 호이스팅의 개념은 이렇게 변경될수 있습니다. 자바스크립트에서 호이스팅은 변수의 선언을 코드 최상단으로 끌어올리는것으로 간주하는것이다. 이때 함수선언문의 경우 몸체도 최상단으로 끌어올려진것처럼 간주한다.

변수의 생명주기

앞선 설명으로 호이스팅에 개념에 대해서는 이해할수 있습니다. 하지만 아직 풀리지 않은 문제가 있습니다. 바로 처음에 undefined 라는 값이 담겨있는것입니다. 이는 호이스팅과 관련이있는 항목이 아닙니다. 호이스팅은 어디까지나 식별자를 수집하는것이기 때문입니다.

이를 이해하기 위해서는 변수의 생명주기에 대해서 이해해야합니다. 변수는 선언, 초기화, 할당 단계를 가집니니다. 항목별로 알아보겠습니다.

  • 선언단계 : 메모리에 변수를 사용할수있도록 할당하는단계
  • 초기화단계 : 변수를 사용할 메모리에 담겨있는 쓰레기값을 제거하는 단계
  • 할당단계 : 프로그래머가 변수에 할당한 값을 메모리에 담는 단계

여기서 선언단계가 런타임이전에 일어나는것이 바로 호이스팅입니다. 이제 예상할수 있겠지만, var로 선언한 변수의 경우 선언이전에 undefined라는 값을 읽을수 있는것은 초기화단계가 호이스팅 직후 엔진에 의해서 바로 수행되기 때문입니다. 따라서 우리가 선언이전에 값을 참고하면 참조에러없이 undefined라는 값을 읽을수 있게됩니다.

es6 이후의 변화 let 과 const

ES6이후에 등장한 새로운 변수 선언방식인 let 과 const는 기존의 변수선언 방식인 var과 다소 다른 특징을 가집니다. 변수명을 중복 선언할수 없고, 블록레벨 스코프를 가지는등의 특징도 가지고 있지만, 호이스팅에 대해서도 차별점을 가지고 있습니다.

이 호이스팅에 대한 차이때문에 let const는 var과 달리 호이스팅이 일어나지 않는다 라고 하는 오해가 있습니다. 물론 이러한 오해가 발생할만한 나름의 근거는 있습니다. 왜냐하면 앞서 테스트 해본것 처럼 코드로 선언이전의 변수를 참고하면 에러가 나기 때문입니다.

console.log(a, b) // ReferenceError: Cannot access 'a' before initialization
const a = 1
const b = 2

하지만 let 과 const 또한 var과 같이 호이스팅을 하고 있습니다. 다만 호이스팅 이후의 절차가 var과 다소 상이하기 때문에 이러한 현상이 발생합니다.

일시적 사각지대(TDZ)

앞서 변수의 선언방식을 설명할때, var 변수는 선언과 초기화 단계가 동시에 진행된다고 하였습니다. 하지만, const let의 경우 선언은 var 처럼 호이스팅되기 때문에 미리 수행되나, 초기화 단계는 실제 코드의 선언문에서 수행됩니다.

따라서 const let의 경우 선언 이전에 호이스팅이 되어 실행컨텍스트는 변수정보를 알고있지만, 프로그래머가 그 변수를 참조할수는 없는것입니다. 이때 선언단계와 초기화 단계사이에 있는 변수는 일시적 사각지대(TDZ)에 있다고 말하며 이과정에 있는 변수는 참조에러를 발생시키게됩니다.

이제 let const 도 호이스팅이 일어나는것은 이해하였을것입니다. 하지만 실제 코드에서 이를 확인해볼수 있다면 더 좋을것 같습니다. 아래 코드를 보면서 호이스팅이 정말로 발생하는지 알아보겠습니다.

let test = 1
{
  console.log(test) //ReferenceError: Cannot access 'test' before initialization
  let test = 2
}

블록레벨 스코프를 가지기 때문에, 만약 호이스팅이 없다면 console.log에는 1이 찍혀야합니다. 하지만 호이스팅이 발생하기 때문에 블록내부에 선언된 test가 호이스팅되어 값을 참조할수 없는것입니다.

마치며

이번 포스트에서 설명한 내용은 사실 잘못 이해하더라도 자바스크립트를 사용하는데는 아무런 문제가 없습니다. 동작 자체를 잘못 이해한것이 아니라 원리에 대해서 오해가 있었기 때문입니다. 하지만 자바스크립트를 보다 잘 사용하기 위해서는 이러한 개념들에 대한 이해는 반드시 필요하다고 생각하기에 이글을 통해 호이스팅에 대해 좀더 잘 이해할수 있었으면 좋겠습니다.

참고자료

호이스팅 - MDN Web Docs 용어 사전: 웹 용어 정의 | MDN 정재남, 『코어 자바스크립트』 . 경기: 위키북스, 2019. 이웅모, 『모던 자바스크립트 Deep Dive』 . 경기: 위키북스, 2020.