프론트엔드

실행 컨텍스트와 스코프 체인

2026. 03. 24. 화요일 오후 9시 10분

들어가기 앞서...

이 글은 ES5 기준으로 작성되었습니다. ES6에서는 ThisBinding 위치, VariableEnvironment와 LexicalEnvironment의 역할 구분 등 일부 스펙이 변경되었습니다. ES6에 대한 실행 컨택스트는 다음 포스트에서 다룰 예정입니다.

JS는 변수랑 함수를 어떻게 관리할까?

자바스크립트로 코드를 작성하다보면, 알 수 없는 복잡한 규칙과 만나게 되는 경우가 참 많습니다.

  • 분명 같은 this 키워드인데, 상황에 따라서 가리키는 객체가 다르다던가...
  • 분명 선언한 적도 없는 변수인데 멀쩡히 참조할 수 있다던가...
  • 내 스코프 밖의 변수인데 아무렇지 않게 참조할 수 있다던가...

물론 이걸 몰라도 어느 정도는 개발할 수 있지만, 하다보면 분명 답답한 순간이 옵니다.

  • 일단 동작하긴 하는데 왜 그렇게 되는지 모른다거나...
  • this가 가리키는 대상을 원하는대로 제어하지 못한다거나...
  • 의도하지 않게 변수가 공유되버려서 온갖 버그를 양산한다거나...

이런 어려움을 극복하려면, 실행 컨택스트를 이해하는게 중요하다고 생각합니다.
그럼 실행 컨택스트가 무엇인지 알아보려고 합니다.

실행 컨텍스트(Execution Context)란?

실행 컨텍스트란 코드를 실행하기 위해 필요한 환경 정보를 담은 객체입니다.

자바스크립트는 코드를 한 줄씩 바로 실행하는 것처럼 보이지만, 실제로는 실행 전에 먼저 환경 정보를 수집하고 준비합니다. 이 환경 정보를 담는 그릇이 실행 컨텍스트입니다.

실행 컨텍스트는 활성화되는 시점에 세 가지 정보를 수집합니다.

  • VariableEnvironment (VE): 초기 환경 정보를 담은 스냅샷
  • LexicalEnvironment (LE): 실행 중 변경 사항이 즉시 반영되는 환경 정보
  • ThisBinding: this가 어떤 객체를 가리키는지에 대한 정보

실행 컨텍스트는 언제 만들어지는가

실행 컨텍스트는 두 가지 시점에 생성됩니다.

  • 자바스크립트를 처음 실행할 때: 전역 실행 컨텍스트 생성
  • 함수가 호출될 때: 함수 실행 컨텍스트 생성

전역 실행 컨텍스트는 스크립트 시작과 함께 생성되어 종료 시까지 유지됩니다. 함수 실행 컨텍스트는 함수가 호출될 때마다 새로 생성되고, 실행이 끝나면 제거됩니다.

콜스택과 실행 컨텍스트

실행 컨텍스트는 콜스택(Call Stack)을 통해 관리됩니다.

1function second() {
2  console.log('second');
3}
4
5function first() {
6  second();
7}
8
9first();
10

위 코드의 실행 흐름을 따라가면 이렇습니다.

  1. 전역 실행 컨텍스트가 콜스택에 쌓인다
  2. first()가 호출되어 first 실행 컨텍스트가 콜스택에 쌓인다
  3. second()가 호출되어 second 실행 컨텍스트가 콜스택에 쌓인다
  4. second 실행이 끝나면 콜스택에서 제거된다
  5. first 실행이 끝나면 콜스택에서 제거된다
  6. 전역 컨텍스트는 스크립트 종료 시 제거된다

콜스택 최상단에 있는 컨텍스트가 현재 실행 중인 컨텍스트입니다. 새로운 함수가 호출되면 이전 컨텍스트는 일시 중단되고, 새 컨텍스트가 최상단에 쌓여 실행됩니다.


VariableEnvironment와 LexicalEnvironment

두 환경은 실행 컨텍스트 생성 시점에는 동일한 내용으로 구성됩니다. 이후 차이가 생깁니다.

  • LexicalEnvironment: 코드 실행 중 변경 사항이 즉시 반영됩니다.
  • VariableEnvironment: 처음 생성된 초기 상태를 유지합니다.

LexicalEnvironment

LexicalEnvironment는 두 가지 구성요소로 이루어져 있습니다.

environmentRecord

현재 실행 컨텍스트 내의 식별자 정보가 저장되는 곳입니다. 매개변수 이름, 함수 선언, 변수명이 여기에 담깁니다.

자바스크립트 엔진은 코드를 실행하기 전에 먼저 이 environmentRecord를 구성합니다. 코드를 실행하기 전에 해당 스코프 안의 모든 변수 이름을 수집합니다.
흔히 말하는 호이스팅이 바로 이것입니다.

호이스팅에 대한 자세한 내용은 호이스팅 포스트를 참고해주세요.

전역 실행 컨텍스트의 경우는 조금 다릅니다. 전역 실행 컨텍스트의 environmentRecord는 전역 객체(브라우저의 경우 window, Node.js의 경우 global)를 직접 사용합니다. 그래서 전역 변수를 var로 선언하면 window 객체를 통해 접근 가능한데, 이게 그 이유입니다.

outerEnvironmentReference

현재 실행 중인 함수가 선언된 당시의 LexicalEnvironment를 참조합니다.

여기서 "선언된 당시"라는 표현이 핵심입니다. 함수가 어디서 호출되든 상관없이, 함수가 작성된 위치 기준의 바깥 환경을 가리킵니다. 이 특성이 나중에 클로저를 이해하는 핵심 원리가 됩니다.

outerEnvironmentReference 덕분에 변수를 찾을 때 현재 스코프에서 시작해 바깥 방향으로 탐색해나갈 수 있습니다.


스코프 체인

변수를 찾는 방식

자바스크립트 엔진이 변수를 사용할 때는 다음 순서로 탐색합니다.

  1. 현재 실행 컨텍스트의 environmentRecord에서 탐색
  2. 없으면 outerEnvironmentReference를 따라 바깥 스코프로 이동
  3. 이 과정을 전역 스코프까지 반복
  4. 전역 스코프에서도 없으면 ReferenceError 발생

이렇게 스코프를 타고 올라가면서 선언을 탐색하는 구조를 스코프 체인이라고 합니다.

예시로 보는 스코프 체인

1var a = 'global';
2
3function outer() {
4  var a = 'outer';
5
6  function inner() {
7    // inner 스코프에는 a가 없음
8    // → outerEnvironmentReference를 통해 outer 스코프에서 탐색
9    console.log('inner >>> ', a);
10  }
11
12  console.log('outer >>> ', a);
13  inner();
14}
15
16outer();
17console.log('global >>> ', a);
18
1outer >>>  outer
2inner >>>  outer
3global >>>  global
4

inner 함수 안에서 a를 선언하지 않았지만, 스코프 체인을 통해 outera를 찾아 읽어옵니다.

주목할 점은 global >>> global입니다. outer 함수 안의 var a = 'outer'는 전역 변수 a와 별개입니다. outer는 자신의 스코프에 새로운 a를 선언하고 사용했기 때문에, 전역 a는 영향을 받지 않았습니다.

스코프는 안에서 바깥으로만 탐색된다

스코프 체인의 중요한 특징은 단방향이라는 점입니다. 안쪽 스코프에서 바깥을 볼 수는 있지만, 바깥에서 안쪽을 볼 수는 없습니다.

1function outer() {
2  var outerVar = 'outer';
3}
4
5console.log(outerVar); // ReferenceError: outerVar is not defined
6

전역 스코프에서 outer 함수 내부의 outerVar에 접근하려 하면 에러가 납니다.


정리

  • 실행 컨텍스트는 코드를 실행하기 위한 환경 정보(VE, LE, ThisBinding)를 담은 객체입니다.
  • 함수가 호출될 때마다 새로운 실행 컨텍스트가 생성되어 콜스택에 쌓이고, 실행이 끝나면 제거됩니다.
  • LexicalEnvironment의 environmentRecord에 현재 스코프의 식별자 정보가 저장됩니다.
  • LexicalEnvironment의 outerEnvironmentReference는 함수가 선언된 당시의 외부 환경을 참조합니다.
  • 변수를 찾을 때는 현재 스코프에서 시작해 outerEnvironmentReference를 따라 바깥으로 탐색하는데, 이것이 스코프 체인입니다.

마치며

실행 컨텍스트는 자바스크립트의 여러 동작을 설명하는 기반 개념입니다. 호이스팅, 스코프, this 바인딩이 모두 실행 컨텍스트와 연결됩니다. 특히 outerEnvironmentReference를 통한 스코프 체인은 클로저의 핵심 원리이기도 합니다. 긴 글 읽어주셔서 감사합니다.