티스토리 뷰
00. TL;DR
자바스크립트의 스코프와 실행 환경은 코드 실행 시 식별자를 어떻게 찾고, 어디까지 접근 가능한지를 결정하는 핵심 메커니즘입니다.
- 스코프(Scope)는 식별자(변수, 함수 등)의 유효 범위를 결정하며, 선언 위치에 따라 전역/지역/블록 스코프로 나뉩니다.
- 스코프는 식별자 탐색 규칙(스코프 체인)을 통해 하위 → 상위로 식별자를 탐색하며, 결국 전역까지 도달하지 못하면 오류를 발생시킵니다.
- 자바스크립트는 렉시컬 스코프(정적 스코프)를 따릅니다. 즉, 선언 시점에 상위 스코프가 결정됩니다.
- 렉시컬 환경(Lexical Environment)은 식별자와 값의 바인딩 정보를 담는 구조로, 실행 컨텍스트 내부 구성 요소입니다.
- 실행 컨텍스트(Execution Context)는 코드 실행을 위한 정보 단위이며, 렉시컬 환경, 변수 환경,
this바인딩을 포함합니다. - 실행 컨텍스트는 코드 실행 시점마다 스택에 쌓이며, 각 컨텍스트는 자신만의 렉시컬 환경을 통해 스코프 체인을 형성합니다.
- 이러한 구조 덕분에 자바스크립트는 모듈화, 식별자 충돌 방지, 변수의 지역화 같은 기능을 안전하게 제공합니다.
01. 스코프
01.01. 스코프란?
스코프(scope)는 자바스크립트 엔진이 식별자(변수, 함수 이름 등)를 검색하고 접근할 수 있는 범위를 의미합니다.
즉, 특정 식별자가 코드의 어느 부분에서 접근이 가능한지 결정하는 규칙입니다.
01.02. 식별자의 유효 범위
식별자의 유효 범위는 해당 식별자가 선언된 위치에 따라 결정됩니다.
스코프의 종류:
- 전역 스코프:
- 전역 스코프는 함수 외부에서 선언된 변수 또는 최상위 스코프에서 선언된 변수를 말합니다.
- 전역 스코프에 선언된 식별자는 코드의 어느 곳에서든 접근할 수 있습니다.
- 지역 스코프:
- 함수 내부에서 선언된 식별자는 지역 스코프를 가집니다.
- 지역 스코프 범위에 선언된 식별자는 해당 함수 내부에서만 접근할 수 있습니다.
- 블록 스코프
let과const키워드는 블록 스코프(Block Scope) 로 동작합니다.- 블록 스코프는 함수, 중괄호(
{}) 기준으로 유효하게 접근할 수 있습니다.
예:
// 전역 스코프
var globalVariable = "전역 변수";
function outerFunction() {
// 지역 스코프 (outerFunction)
var outerVariable = "외부 함수 변수";
console.log(globalVariable); // 접근 가능
function innerFunction() {
// 지역 스코프 (innerFunction)
var innerVariable = "내부 함수 변수";
console.log(globalVariable); // 접근 가능
console.log(outerVariable); // 접근 가능
console.log(innerVariable); // 접근 가능
}
innerFunction();
console.log(innerVariable); // 접근 불가능 (innerFunction 스코프)
}
outerFunction();
console.log(outerVariable); // 접근 불가능 (outerFunction 스코프)
console.log(globalVariable); // 접근 가능
// 블록 스코프 (if 문)
if (true) {
let blockVariable = "블록 변수";
console.log(blockVariable); // 접근 가능
}
console.log(blockVariable); // 접근 불가능 (블록 스코프)
// 블록 스코프 (for 문)
for (let i = 0; i < 3; i++) {
console.log(i); // 접근 가능
}
console.log(i); // 접근 불가능 (블록 스코프)
01.03. 스코프는 식별자 탐색 규칙이다
자바스크립트 엔진은 코드를 실행 중에 식별자를 만나면 해당 식별자가 어떤 변수나 함수를 참조하는지 확인하는 과정을 거치는데, 이때 스코프는 식별자를 찾는 탐색 규칙이 됩니다.
탐색 순서:
- 자바스크립트 엔진은 실행중 식별자를 만나면
- 현재 실행중인 위치의 스코프에서 부터 식별자 탐색합니다.
- 만약 현재 스코프에서 식별자를 찾지 못하면 상위 스코프로 이동하여 탐색을 계속합니다.
- 상위 스코프에서도 찾지 못하면 그 상위 스코프로 이동하여 탐색을 시작합니다.
- 가장 바깥쪽 스코프인 전역 스코프까지 탐색해도 식별자를 찾지 못한다면
ReferenceError가 발생합니다.
위와 같이 스코프들이 연결되어 있는것을 스코프 체인(Scope Chain)이라고 합니다.
예시:
var globalVar = "전역 변수";
function outerFunc() {
var outerVar = "외부 함수 변수";
function innerFunc() {
var innerVar = "내부 함수 변수";
console.log(innerVar); // 현재 스코프에서 찾음
console.log(outerVar); // 상위 스코프(outerFunc)에서 찾음
console.log(globalVar); // 상위 스코프(전역)에서 찾음
}
innerFunc();
}
console.log(globalVar); // ✅ 전역 스코프에서 찾음
outerFunc();
// console.log(outerVar); // ❌ 오류 발생 (스코프 체인에서 찾을 수 없음)
01.04. 선언 시점에 스코프가 결정된다
자바스크립트의 스코프는 함수가 선언된 시점에 정적으로 결정됩니다.
선언 시점에 결정되기 때문에 렉시컬 스코프 또는 정적 스코프이라고 합니다.
스코프 결정은 함수 호출에 결정되는 this 의 동적 결정과는 반대라고 볼 수 있습니다.
var value = 1;
function first() {
var value = 10;
second();
}
function second() {
console.log(value);
}
// 출력 결과: 1
// second 함수가 선언될 때의 전역 스코프의 value를 참조
first();
02. 코드의 문맥과 실행 환경
02.01. 렉시컬 환경(Lexical Environment)
렉시컬 환경(Lexical Environment)은 식별자와 해당 식별자에 대한 바인딩(값 또는 메모리 주소)을 기록하는 환경 정보에 대한 개념적인 표현입니다.
렉시컬 환경의 구성:
- 환경 레코드(Environment Record):
- 현재 스코프 내에서 선언된 식별자(변수, 함수 선언 등)와 값을 저장하는 객체입니다.
- 외부 렉시컬 환경 참조(Outer Lexical Environment Reference):
- 외부 스코프의 렉시컬 환경을 가리키는 참조입니다.
- 스코프 체인이 가능한 이유는 외부 렉시컬 참조 정보가 존재하기 때문입니다.
- 전역 렉시컬 환경에서의 외부 렉시컬 환경 참조는
null을 가리킵니다.
렉시컬 환경은 자바스크립트 엔진의 코드 파싱 과정 중 생성합니다.
02.02. 실행 컨텍스트(Execution Context)
실행 컨텍스트(Execution Context)는 자바스크립트 코드가 실행될때 생성되는데, 전역 실행 컨텍스트와, 함수 단위로 실행 컨텍스트가 생성됩니다.
실행 컨텍스트에는 코드를 실행하기 위한 정보가 포함되어 있습니다.
02.02.01. 실행 컨텍스트의 구성
- 변수 환경(Variable Environment)
- 현재 실행 컨텍스트에서 선언된 식별자(변수, 함수 등)에 대한 정보를 담는 구조입니다.
- 본질적으로는 렉시컬 환경(Lexical Environment)과 동일한 구조를 가지며, 초기 생성 시점에서만 별도로 존재합니다.
- 렉시컬 환경(Lexical Environment)
- 위에서 설명한 렉시컬 환경과 같은 개념입니다.
- 실행 중인 코드에서 식별자를 검색할 때 실제로 사용되는 환경입니다.
- 초기에는 변수 환경과 동일하지만,
catch블록이나with문 같은 동적 스코프 변경이 발생하면 별도로 분리되어 작동할 수 있습니다.
this바인딩(This Binding)this키워드가 참조할 객체를 나타냅니다.this의 값은 함수 호출 방식(일반 호출, 메서드 호출, 생성자 호출 등)에 따라 동적으로 결정됩니다.
자바스크립트 엔진은 코드를 실행하기 전에 실행 컨텍스트 스택(Execution Context Stack)을 관리합니다.
02.02.01.01. 변수 환경 vs 렉시컬 환경의 차이점과 관계
자바스크립트 실행 컨텍스트 내부에는 두 가지 유사한 구조가 존재합니다:
변수 환경(Variable Environment)과 렉시컬 환경(Lexical Environment)입니다.
두 환경은 실행 컨텍스트 생성 시 동일한 정보를 가지고 시작하지만, 역할과 동작 시점에서 중요한 차이가 있습니다.
02.02.01.01.01. 변수 환경 (Variable Environment)
- 실행 컨텍스트 생성 시, 해당 스코프에서 선언된 변수 및 함수 정보를 담습니다.
var, 함수 선언문처럼 호이스팅되는 식별자들의 초기값(undefined) 또는 참조를 저장합니다.- 비유하자면, 집을 짓기 위한 설계도처럼 미리 방(식별자)의 존재와 구조를 정의하는 역할을 합니다.
02.02.01.01.02. 렉시컬 환경 (Lexical Environment)
- 변수 환경과 같은 구조로 생성되며, 초기에는 동일한 객체를 참조합니다.
- 실행 중 실제로 식별자를 탐색하고 값을 읽거나 쓸 때 사용하는 환경입니다.
- 실행 도중
let,const로 선언된 변수들은 여기에 저장되며, 값의 실시간 변경 상태를 반영하는 작업 공간이라 볼 수 있습니다.
02.02.01.01.03. 왜 두 환경으로 나눴는가?
실행 컨텍스트 안에서 굳이 변수 환경과 렉시컬 환경을 분리한 이유는,
자바스크립트의 스코프가 동적으로 변하는 예외 상황을 정확하게 처리하기 위해서입니다.
예외 상황 1: catch 블록
try {
throw new Error("오류");
} catch (error) {
console.log(error); // ✅ 렉시컬 환경 안에서만 유효
}
console.log(error); // ❌ ReferenceError
catch는 오직 블록 내부에서만 유효한 변수(error)를 생성합니다.- 이걸 구현하기 위해 별도의 렉시컬 환경이 생성되어 기존 스코프와 분리됩니다.
- 변수 환경은 건드리지 않고, 렉시컬 환경만 임시로 분기되어 안전한 스코프를 구성합니다.
예외 상황 2: with 문 (사용 지양)
var obj = { a: 10 };
var a = 100;
with (obj) {
console.log(a); // 10 (obj.a)
}
with문은 특정 객체의 프로퍼티들을 스코프처럼 접근하게 만듭니다.- 이때도 새로운 렉시컬 환경을 생성하여 체인에 끼워 넣습니다.
- 변수 환경은 원래대로 유지되고, 현재 탐색용 환경만 임시로 조작됩니다.
02.02.01.01.04. ES6 이후 렉시컬 환경의 역할 강화
ES6 이전에는 var만 존재했고, 대부분의 변수는 함수 스코프로만 처리되었습니다.
렉시컬 환경은 사양에는 있었지만, 주로 내부 동작에서만 활용되었습니다.
하지만 ES6 이후 let, const, TDZ(Temporal Dead Zone), 블록 스코프, 클래스, 모듈 등의 도입으로 렉시컬 환경이 실질적으로 구분되어야 할 필요가 생겼습니다.
let과const는 선언되었지만 초기화되기 전까지 접근이 불가능한 TDZ 구간을 가집니다.
이처럼 "존재하지만 아직 초기화되지 않은 식별자 상태"를 관리하려면,
실행 시점 식별자 정보를 따로 관리하는 렉시컬 환경이 반드시 필요합니다.
02.02.01.01.05. 요약
"선언"은 → 변수 환경(Variable Environment)에 등록되고, "값의 할당/변경"은 → 렉시컬 환경(Lexical Environment)에 반영됩니다.
| 구분 | 변수 환경 | 렉시컬 환경 |
|---|---|---|
| 초기 상태 | 실행 컨텍스트 생성 시 만들어짐 | 변수 환경과 같은 객체로 시작 |
| 주요 용도 | 선언된 식별자 정보 저장 (var, 함수 선언) |
실행 중 식별자 탐색 및 값 관리 (let, const) |
| 변경 여부 | 고정됨 | 필요 시 새로운 렉시컬 환경으로 분리 |
| TDZ 관리 | ❌ 불가 | ✅ 가능 |
| 특수 상황 처리 | 사용되지 않음 | catch, with, eval 등에서 별도 분리 필요 |
02.02.02. 실행 컨텍스트의 종류
- 전역 실행 컨텍스트(Global Execution Context):
- 자바스크립트 코드가 처음 실행될 때 가장 먼저 생성됩니다.
- 각 환경(Window, Node) 의 전역 객체가
this에 바인딩됩니다.
- 함수 실행 컨텍스트(Function Execution Context):
- 함수가 호출될 때마다 생성됩니다.
- Eval 실행 컨텍스트(Eval Execution Context):
eval()함수를 사용하여 코드를 실행할때 마다 생성됩니다.
02.03. 렉시컬 환경과 실행 컨텍스트의 관계
- 렉시컬 환경은 실행 컨텍스트의 구성 요소 중 하나입니다.
- 각 실행 컨텍스트는 자신만의 렉시컬 환경을 가집니다.
- 실행 컨텍스트의 렉시컬 환경은 스코프 체인을 구성하는 역할을 수행합니다.
02.04. 스코프가 왜 필요할까?
다음과 같이 네임스페이스로서의 역할로서 필요합니다.
- 식별자 충돌 방지:
- 동일한 이름의 식별자에 대해서 스코프가 다르다면 서로 다르게 인식 할 수 있도록 합니다.
- 변수의 유효 범위 제한:
- 스코프를 통해 변수의 접근 범위가 제한되기 때문에, 특정 영역(스코프) 한정으로 변수를 관리할 수 있게 됩니다.
- 모듈화:
- 영역에 대한 기준이 생기기 때문에 모듈단위의 개발이 가능하게 됩니다.
즉, 스코프는 파일 시스템의 디렉토리와 비슷한 역할을 수행한다고 볼 수 있습니다.
03. 동적 스코프 vs 렉시컬 스코프
스코프는 식별자를 어떻게 찾을 것인가에 대한 규칙이라고 언급했었습니다.
이 규칙에는 크게 두 가지 방식이 있습니다
03.01. 렉시컬 스코프 (Lexical Scope 또는 정적 스코프)
렉시컬 스코프는 함수가 선언될 때 시점에 따라 정적으로 결정되는 스코프 방식을 말합니다.
03.02. 동적 스코프 (Dynamic Scope)
동적 스코프는 함수가 호출될 때 어디에서 호출 되었는지에 따라 동적으로 결정되는 스코프 방식을 말합니다.
개념적인 예시 (자바스크립트는 동적 스코프를 사용하지 않습니다):
만약 자바스크립트가 동적 스코프를 사용한다면, 다음과 같이 동작할 수 있습니다.
var a = "aaa";
function fn1() {
console.log(a); // 출력: 실행 시점에 a에 대한 스코프가 결정되어 'bbb' 가 출력
}
function fn2() {
var a = "bbb";
fn1();
}
fn2();
03.03. 렉시컬 스코프와 동적 스코프 비교
| 특징 | 렉시컬 스코프 (Lexical Scope) | 동적 스코프 (Dynamic Scope) |
|---|---|---|
| 스코프 결정 시점 | 함수 선언 시점 | 함수 호출 시점 |
| 스코프 결정 기준 | 함수가 선언된 주변 코드 구조 | 함수가 호출된 주변 환경 |
| 스코프 체인 형성 | 선언 시점의 상위 스코프 기반 | 호출 시점의 호출 스택 기반 |
| 예측 가능성 | 높음 | 낮음 |
| 사용 언어 | 자바스크립트, C++, Python 등 | Bash, 일부 Lisp 방언 등 |
'books > deep-dive' 카테고리의 다른 글
| [Deep-Dive JavaScript] 22장 - this (0) | 2025.06.12 |
|---|---|
| [Deep-Dive JavaScript] 19장 - 프로토타입 (0) | 2025.03.26 |
| [Deep-Dive JavaScript] 12장 - 함수 (0) | 2025.03.26 |
| [Deep-Dive JavaScript] 10장 - 객체 리터럴 (0) | 2025.03.25 |
| [Deep-Dive JavaScript] 6장 - 데이터 타입 (0) | 2025.03.24 |
- Total
- Today
- Yesterday
- premitive
- 바이트 코드
- primitive
- nuxt
- double-linked-list
- TypeScript
- prototype
- JavaScript
- 모노레포 스크립트
- string
- refrerence
- deep dive
- ViTE
- react
- interning
- library mode
- string table
- vee-validate
- npm ci
- webpack
- JIT
- bundler
- scoped slot
- vue
- react-router
- useasyncdata
- object literal
- uselazyasyncdata
- pnpm 명령어
- pakage-lock.json
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 |
