[MDN Study] JavaScript 배경지식 초급 2

기술 노트
2025. 12. 15. 14:41
2026. 2. 13. 17:39

1. 개요

JavaScript에 등장하는 개념인 호이스팅, 컨텍스트, 스코프, this, 콜 스택에 대해 다룬다.

2. 들어가기에 앞서

ES6로 넘어오면서, ES5에서 드러났던 불편한 점들을 개선하기 위해 기존의 개념적 모델만으로는 설명하기 어려운 새로운 규칙들이 도입되었다.

이러한 변화는 ES5에서 주로 지적되었던 문제점들을 해결하는 데 분명히 효과가 있었지만, 하나의 언어 안에 두 세대의 규칙이 공존하게 되었다.

기존 코드와의 호환성을 확보해야 했던 JS 입장상 이는 불가피한 선택이었으나, JS의 구조에 아직 익숙하지 않은 개발자들이 개념을 이해하는 데 있어 혼동을 겪는 원인이 되었다.

그렇다고 이 개념들을 건너뛰기에는 이들이 JS가 동작하는 핵심 메커니즘을 이루고 있고, 엔진이 코드를 해석 및 실행하는 과정에서 유기적으로 연결되어 작동하기 때문에, 각 개념을 분리하여 설명하는 것 또한 쉽지 않다.

따라서 이곳에서는 ES5의 개념적 모델링을 기준으로 JS의 실행 구조에 대한 기반을 다진 다음, ES6로 넘어오면서 추가된 규칙들과 예외들이 기존 모델에 어떻게 얹혀있는지 살핀다.

이를 통해 각자의 개념들이 어떻게 관계되고 연결되어 있는지 이해하는 것을 목표로 한다.

3. 호이스팅

JS로 개발을 하다 보면 간혹 마주칠 수 있는 단어이다.

자바스크립트에서 호이스팅이란 엔진이 코드를 실행하기 전에 변수 선언문(값 할당은 X)과 함수 선언문(함수 표현식은 X[^1])이 실행 컨텍스트의 최상단으로 끌어올려진 것처럼 동작하는 현상을 말한다.

ES5의 개념적 모델에서는 실행 컨텍스트 생성 시 변수 객체에 변수 선언과 함수 선언이 등록된다. 이 때 변수의 초기값은 undefined이고, 함수는 선언문 그대로 객체가 설정된다.

이게 호이스팅 현상의 원인인데, 나중에 변수나 함수를 선언 이전에 참조하더라도 JS 엔진 차원에서 일관된 동작이 보장되는 효과를 얻었고, 결과적으로 실행 안정성과 개발 편의성을 확보하게 되었다.

위의 개념들을 처음 보는 사람이라면 실행 컨텍스트, 변수 객체 등의 개념이 익숙하지 않을 것이다.

실행 컨텍스트가 무엇인지부터 한번 살펴보자.

4. 실행 컨텍스트

실행 컨텍스트는 JS 코드가 실행되기 위해 필요한 환경 정보들을 묶어 관리하는 개념이다. 간단히 비유하면 무대 혹은 토대라고 할 수 있다. 함수가 호출될 때마다 그 함수의 새 실행 컨텍스트가 생성된다.

4.1. 종류

실행 컨텍스트는 다음 세 가지 종류가 있다.

  • 전역 실행 컨텍스트 JS 코드가 실행될 때 가장 먼저 생성된다. 전역 변수·함수를 관리한다. 전역 객체(window, global), this가 포함된다.

  • 함수 실행 컨텍스트 함수가 호출될 때마다 새롭게 생기는 컨텍스트이다. 지역 변수, 인수, 내부 함수를 포함한다.

  • eval() 실행 컨텍스트 eval() 함수는 실행 컨텍스트와 상호작용하지만, 현재는 eval() 자체가 사장되어가는 기능이라 다루지 않는다.

4.2. 구성(ES5 기준)

ES5에서 실행 컨텍스트는 다음 세 가지로 구성된다.

변수 객체 (Variable Object, VO) 컨텍스트 내에서 선언된 변수, 함수 선언, 인수 등을 포함한다. 전역 컨텍스트의 경우 전역 객체 === 변수 객체이다. 함수 컨텍스트의 경우 활성 객체(Activation Object)[^2]가 변수 객체 역할을 한다.

> Q. 그러면 전역 객체를 부르면 전역 스코프에 선언된 변수, 함수도 같이 등장하나? > A. 그렇다. 전역에서 선언한 변수, 함수는 전역 객체의 프로퍼티로 등록된다. > 예를 들어 전역에서 var x = 0; function a() {...} 이렇게 작성한 경우, > window.x나 window.a로 호출할 수 있다. > > 단, ES6에서 추가된 let, const는 전역 렉시컬 환경에 따로 저장되기 때문에 불가능하다. > 함수 표현식(const a = function () {...})으로 선언된 함수도 동일하다. > var과 함수 선언식만 해당된다.

> ES6에서는 ES5의 변수 객체 개념이 정리되어 변수 환경과 렉시컬 환경 개념으로 대체되었다. > ES6 명세서에서는 VO가 등장하지 않는다.

스코프 체인 (Scope Chain) 현재 컨텍스트와 관련된 변수 객체들로 구성된 리스트이다. 코드의 유효 범위를 결정하고 변수와 함수의 검색을 담당한다. 상위 컨텍스트의 변수를 참조할 때 이곳을 이용한다.

this binding 실행 컨텍스트 내에서 this가 가리키는 값을 결정하는 규칙이다. 전역 컨텍스트에서 this는 전역 객체이고, 함수 컨텍스트에서는 함수 호출 방식에 따라 다르다.

4.3. 구성(ES6 기준)

ES6에서 실행 컨텍스트는 다음 세 가지로 구성된다.

렉시컬 환경 (Lexical Environment) 현재 컨텍스트의 식별자(변수, 함수, 클래스 등의 이름)와 그 바인딩을 관리한다. let, const, class와 같은 블록 스코프 선언은 선언적 환경 레코드에, 전역 수준의 함수 선언과 전역 객체의 프로퍼티는 객체 환경 레코드에 저장된다.

렉시컬 환경은 환경 레코드와 외부 환경 참조 다음 두 가지로 구성된다.

환경 레코드 (Environment Record) 식별자와 값이 저장되는 곳이다. 실행 컨텍스트가 전역인지 아닌지에 따라 전역 환경 레코드와 함수 환경 레코드로 분류한다. 전역 환경 레코드는 단 1개만, 함수 환경 레코드는 함수 컨텍스트가 생성될 때마다 만들어진다.

외부 환경 참조 (Outer Environment Reference) 상위 렉시컬 환경을 가리키는 링크(참조)를 말한다. 외부 스코프(=상위 렉시컬 환경)에 대한 참조로서 스코프 체인을 형성하는데, 이것으로 외부 스코프의 변수, 함수에 접근할 수 있다. 이런 구조를 통해 함수가 자신이 생성된 렉시컬 환경을 기억하고 접근할 수 있을 때 이것을 클로저라 부른다.

전역 환경 레코드에서는 var, let, const로 선언된 전역 변수, 전역 함수, 빌트인 프로퍼티 등을 관리한다.
아래와 같은 구조를 가진다.

선언적 환경 레코드 let, const처럼 블록 스코프를 가지는 변수를 저장한다. 식별자를 키로 사용하며, 각 키는 고유한 값을 저장한다. 코드 블록의 스코프를 관리하는 데에 사용되면 외부 렉시컬 환경을 참조하여 스코프 체인에서 상위 스코프를 찾는다.

객체 환경 레코드 주로 전역 객체나 일부 특정 객체(with문으로 지정된 객체 등)를 기반으로 식별자를 관리하는 환경 레코드이다. 식별자를 실제 객체 속성에 매핑하는 역할을 맡는다. 전역 컨텍스트에서는 var 선언과 함수 선언이 전역 객체의 프로퍼티(속성)로 등록된다. 전역 컨텍스트에서 this가 전역 객체를 가리키는 동작과도 연관되어 있다.

함수 환경 레코드에서는 함수 매개변수, let, const로 선언된 지역 변수, 함수 선언문을 관리한다.
또한 arguments 객체, super 바인딩, this 바인딩 정보도 관리한다.
함수 환경 레코드는 기본적으로 선언적 레코드의 성격을 띠며, 내부에 객체 환경 레코드를 별도로 두지 않는다.
with 문이나 catch 절을 사용할 때 새로운 렉시컬 환경을 통째로 하나 더 만들어 스코프 체인에 끼워넣는 방식으로 동작한다.
아래와 같은 구조를 가진다.

선언적 환경 레코드 전역 환경 레코드와 맥락은 동일하나, 이곳에서는 지역 변수·함수를 관리한다.

변수 환경 (Variable Environment) ES6에서 var을 새로운 방식과도 호환시키기 위해 유지되는 전용 렉시컬 환경이다. 정확히는 컨텍스트 생성 시점의 var 바인딩 상태를 렉시컬 환경과 분리하여 스냅샷처럼 유지하는 곳이라고 할 수 있다. var은 함수 스코프이기 때문에 블록 스코프(let, const)와 구분해서 관리해야 하기 때문이다. 구조 자체는 렉시컬 환경과 동일하다.

이 글을 읽다 보면 var과 let·const는 하나로 묶여 설명되지 않고 따로 노는 느낌을 받았을 것이다. let과 const는 블록 스코프의 흐름에 따라 생성되고 소멸되는데, var은 이 흐름을 따르지 않고 함수 스코프의 흐름을 따른다. var의 특이한 특징들은 이 특성에 기인한다.

코드 실행 중에 스코프 구조가 변해도, var 바인딩만큼은 처음 상태를 안정적으로 유지해야 할 필요가 있었고, 컨텍스트 생성 시점의 렉시컬 환경 스냅샷이라는 설명은 이것들을 압축한 것이다.

this 바인딩 실행 컨텍스트가 생성될 때 결정되는 this가 가리키는 값을 말한다. this는 함수를 어디서 어떻게 호출했는지에 따라 달라진다.

4.4. 정리

ES6에서 실행 컨텍스트의 구성

구성 요소

설명

주요 특징

렉시컬 환경(Lexical Environment)

현재 컨텍스트의 식별자(변수, 함수 등)와 바인딩을 관리

블록 스코프(let, const, class)를 실시간으로 반영• 코드 실행에 따라 변경 사항이 계속 업데이트됨

변수 환경(Variable Environment)

var 선언 처리를 위한 레거시 호환용 렉시컬 환경

함수 스코프(var)를 따름• 컨텍스트 생성 시점의 var 바인딩 상태를 스냅샷처럼 유지• 렉시컬 환경과 구조는 동일

this 바인딩(this Binding)

함수 호출 방식에 따라 결정된 this 값 저장

• 실행 컨텍스트가 생성될 때 결정됨• 전역에서는 전역 객체, 함수에서는 호출 주체에 따라 달라짐

렉시컬 환경의 구성

구성 요소

역할

구조적 의미

환경 레코드(Environment Record)

실제 식별자와 값이 저장되는 저장소

• 변수명, 함수명 등의 키(Key)와 값(Value)을 매핑• 스코프 내의 실질적인 데이터를 관리

외부 환경 참조(Outer Environment Reference)

상위 스코프(부모 렉시컬 환경)로의 연결 링크

스코프 체인 형성의 핵심• 현재 스코프에 없는 변수를 상위에서 찾을 수 있게 함• 클로저 구현의 기반

환경 레코드의 분류

레코드 종류

저장 대상

특징

선언적 환경 레코드(Declarative ER)

let, const, class• 지역에서 var로 선언된 변수• 함수 선언, 지역 변수 등

블록 스코프를 따르는 변수 관리• 식별자가 키가 되고, 고유한 값을 직접 저장

객체 환경 레코드(Object ER)

• 전역에서 var로 선언된 변수• 전역 함수, 빌트인 프로퍼티• with문 객체

• 식별자를 특정 객체(예: 전역 객체 window)의 **속성(Property)**으로 매핑• 전역 객체의 동작 방식과 밀접하게 연관

전역 환경 레코드 VS 함수 환경 레코드 비교

구분

전역 환경 레코드 (Global ER)

함수 환경 레코드 (Function ER)

생성 시점

시작 시 단 1개 생성

함수가 호출될 때마다 매번 생성

내부 구조

복합 구조(객체 환경 레코드 + 선언적 환경 레코드)

단일 구조(주로 선언적 환경 레코드 형태)

객체 환경 레코드 역할

var, 전역 함수, 빌트인 객체 관리• 전역 객체(Global Object)와 연결됨

• 일반적으로 없음• with, catch 사용 시에만 동적으로 생성되어 스코프 체인에 추가됨

선언적 환경 레코드 역할

• 전역 스코프의 let, const, class 관리

• 매개변수, arguments 객체• 지역 내 var, let, const, 함수 선언문 관리

특수 관리 항목

• 전역 객체 프로퍼티 바인딩

super 바인딩 (클래스/메서드)• new.target (생성자 호출 여부)