실행 컨텍스트?

Shawn·2026년 1월 20일

JS 딥다이브

목록 보기
1/2
post-thumbnail

1. 들어가며

자바스크립트를 공부를 하게 될 때,
무조건 꼭 마주치게 되는 파트가 실행 컨텍스트이다.

자바스크립트를 사용하는 개발자라면,
실행 컨텍스트라는 개념에 대해서 잘 알고 있는 것이 중요하다.
이 개념을 이해하는 것은 단순한 지식으로 남는 것이 아닌,
실제 개발하는 과정에서 큰 도움을 주기 때문이다.

코드를 디버깅을 할 때 상황을 보면,
왜 이 변수는 이 값이 되는 것인지?
왜 이 함수는 바로 여기서 호출되었는지?
와 같은 궁금증이 생기는 상황이 있다.

이러한 궁금증들을 이해하기 위해서는,
실행 컨텍스트에 대해 알게 되면 의문점들을
훨씬 더 쉽게 해결할 수 있게 된다.

변수의 스코프(Scope), 호이스팅(Hoisting), 함수 호출 방식 등을 이해하면,
코드의 흐름을 좀 더 명확하게 파악하는데 큰 도움이 된다.

또한 코드를 작성할 때 이점이 있다.
어떻게 코드가 실행되는지, 메모리에 어떻게 할당되는지 등등
이러한 것들을 아는 것은 효율적이고 클린한 코드를
작성하는 데에 있어서 도움이 된다.
이는 웹 애플리케이션의 전반적인 성능 향상으로 이어진다.

마지막으로 다른 프로그래밍 기술을 익히는 데에도
실행 컨텍스트의 이해는 필수적이라고 볼 수 있다.
클로저(Closure), 콜백(Callback), 프로미스(Promise)
자바스크립트 고급 개념들을 모두 실행 컨텍스트와 밀접하게 연결되어 있다.

결론적으로 해당 개념들을 제대로 파악하고 싶다면,
실행 컨텍스트를 잘 이해하는 것은 필수적이라고 볼 수 있다.

2. 실행 컨텍스트

자바스크립트는 싱글 스레드 인터프리터 언어로,
각 브라우저는 자체 자바스크립트 엔진을 사용하여 코드를 스캔하고 해석한다.
이 과정에서 중요한 역할을 하는 것이 실행 컨텍스트(Excution Context)인데,
실행 컨텍스트는 코드의 변환과 실행을 담당하는 환경으로,
실행할 코드에 필요한 모든 정보를 담은 객체라고 할 수 있다.

쉽게 말해서 자바스크립트 엔진이 코드를 실행할 때,
1. 지금 어떤 변수들이 있는지
2. 이 함수는 어디에서 호출되었는지
3. this는 무엇인지

관리하는 일종의 실행 공간 이라고 이해하면 편하다.

자세하게 말해보자면, 실행할 코드에
전달할 정보를 담고 있는 객체는 아래와 같이 볼 수 있다.

  1. 변수 - 전역변수, 지역변수, 매개변수, 객체의 프로퍼티
  2. 함수 (Function)
  3. 호이스팅 (Hoisting)
  4. 클로저 (Closure)
  5. 스코프 (Scope)
  6. This

이러한 방법으로 자바스크립트는 해당 스크립트 코드에 있는
실행 컨텍스트에 관한 정보들을 수집한다.

모아진 실행 컨텍스트들을 콜 스택에 쌓아 올린 후,
실행하여 코드의 실행환경 순서를 보장하게 된다.

3. 실행 가능한 코드 종류

실행 가능한 코드에 제공할 환경 정보를 모아놓은 객체라고 볼 수 있는데,

여기서 말하는 실행 가능한 코드는 아래와 같이 존재한다.

전역 코드(Global Code) : 모든 영역에 존재하는 코드
Eval 코드 : eval 함수로 실행되는 코드
함수 코드(Function Code) : 함수 내에 존재하는 코드

4. 콜 스택(Call Stack)

  1. 전역 컨텍스트가 생성되어 콜 스택에 PUSH 된다.
  2. second() 함수를 호출하면, 해당 함수의 새 실행 컨텍스트가
    생성되어 콜 스택의 최상단에 PUSH 된다.
  3. second 내부에서 first()를 호출하면, first의 실행 컨텍스트도
    콜 스택의 최상단에 PUSH 된다.
  4. 이후 second의 실행 컨텍스트도 스택에 POP되며,
    마지막으로 전역 컨텍스트가 남는다.
  5. 최종적으로 전역 컨텍스트도 스택에서 POP되며 프로그램의 실행이 종료된다.

사진처럼 자바스크립트 엔진은 생성된 컨텍스트들을
스택(Stack)이라는 구조로 관리하게 된다.
이러한 구조를 콜 스택(Call Stack) 이라고 불리는데,
위 사진을 아래 내용으로 순서대로 말하자면 이렇게 된다.

  1. 전역 코드를 실행하며 전역 컨텍스트를 스택에 쌓는다.
  2. 함수 호출 시, 해당 함수의 함수 컨텍스트를 생성하여 스택 맨 위에 추가(PUSH)한다.
  3. 함수 실행이 끝나면 해당 컨텍스트를 제거(POP)한다.

위 콜 스택 이미지를 아래와 같이 코드로 작성하면,

var a = "a";

function first() {
 console.log("This is first");
}

function second() {
 first();
}

second();

해당 코드와 이미지는 처음에 자바스크립트가 실행되고
콜스택에 어떤 식으로 컨텍스트들이 담기고,
작동하게 되는지에 대한 예시이다.

전역 컨텍스트는 코드 내부에서 별도의 실행 명령이 없어도
브라우저에서 자동으로 실행하므로, 자바스크립트 파일이 열리는 순간
전역 컨텍스트가 활성화된다고 이해할 수 있다.

⁉️ 참고해주세요!
전역 컨텍스트(Global Context)의 경우,
브라우저 환경 = Window 객체로 표현된다.
Node.js 환경 = Global 객체로 표현된다.

실행 컨텍스트가 생성되는 경우
즉, Call Stack에 실행 컨텍스트가 쌓이게 되는 기준은 아래와 같다.

  • 전역 공간(Global EC) -> 자동생성
  • 일반 함수 실행
  • eval 함수 실행
  • Block 생성

5. 실행 컨텍스트 내부 구조

하나의 실행 컨텍스트는 크게 3가지로,
구성이 되어 아래와 같이 생성된다.

5-1. Variable Environment (변수 환경)

실행에 필요한 여러 정보들을 담을 객체를 생성한다.

Variavble Environment는 아래와 같이 정보를 담는 객체이다.

  1. 변수(Variable)
  2. 매개변수(Parameter), 인수(Arguments)
  3. 함수 선언(함수 표현식X)

이 환경은 environmentRecordouterEnvironmentReference로 구성된다.

  • environmentRecord
    실행 컨텍스트 내에서 선언된 변수와 함수 선언들의 실제 값을 저장한다.
  • outerEnvironmentReference
    외부 환경에 대한 참조를 유지한다.
    이 참조는 현재 컨텍스트가 위치한 코드의 외부 스코프,
    즉 부모 스코프에 대한 정보를 가진다.

실행 컨텍스트가 최초 생성될 때,
Variable Environment에 이 정보들이 저장되고,
이후 Lexical Environment를 형성하기 위해 복사된다.

Variable Environment는 초기화 시점의 상태를 스냅샷으로 유지하며,
후에는 참조만 제공한다. 이는 실행 컨텍스트의 초기 상태를 추적할 때 유용하다.

5-2. Lexical EnvironMent (렉시컬 환경)

Variable Environment와 마찬가지로,
Lexical EnvironmentenvironmentRecordouterEnvironmenReference로 구성된다.

Lexical Environment에서의 environmentRecord에서는
실행 중에 코드 내에서 발생하는 변화를 실시간으로 반영한다.
이는 블록 내의 변수 할당이나 함수 표현식과 같은 동적인 활동을 포함한다.

outerEnvironmentReference
Lexical Environment의 외부 스코프에 대한 참조를 제공한다.
이는 현재 컨텍스트가 어떤 외부 스코프와 연결되어 있는지를 나타낸다.

Lexical Environment는 코드 실행 중, 발생하는 동적인 활동을
반영함으로써 Variable Environment와 차별화가 된다.
이를 통해 실행 컨텍스트의 실시간 상태를 보다 정확하게 반영하게 된다.

Environment 구성

📦 environmentRecord(환경 레코드)

  • 함수 내의 코드가 실행되기 전, 현재 컨텍스트에 관련된
    모든 식별자 정보(매개변수의 이름, 함수 선언, 변수명 등)가 여기에 저장된다.
  • 이 과정을 통해 JavaScript 엔진은 코드 실행 전에
    해당 환경의 식별자들을 인지하게 되며,
    이것이 바로 호이스팅(Hoisting)이다.
    호이스팅은 코드에서 선언들을 먼저 처리하고,
    나중에 할당을 수행하는 JavaScript의 동작을 추상화한 개념이다.
    함수 선언문은 전체가 호이스팅되지만,
    함수 표현식은 이름만 호이스팅되고 함수 본문은
    실행 흐름이 해당 위치에 도달했을 때 처리가 된다.

📄 outerEnvironmentReference(외부 환경 참조)

  • 이는 현재 실행 컨텍스트의 상위 스코프를 참조한다.
  • 다시 말해서, 현재 environmentRecord 외부에 있는 LexicalEnvironment를 참조하는 것으로,
    이를 통해 해당 실행 컨텍스트를 생성한 함수의 외부 환경에 접근할 수 있다.
  • 코드에서 변수를 찾을 때 현재 컨텍스트에서 LexicalEnvironment를 먼저 탐색하고,
    그곳에서 찾지 못하면 outerEnvironmentReference를 통해 상위 스코프를 검색한다.
    이 검색은 전역 컨텍스트의 LexicalEnvironment에 도달할 때까지 계속되며,
    결국 해당 변수를 찾지 못하면 undefined를 반환한다.

5-3. this binging (this 바인딩)

🔗 바인딩이란?

  • 프로그래밍에서 바인딩은,
    식별자(ex : 변수, 함수 이름)를 그들이 대표하는 값과 연결하는 과정을 의미한다. 예를 들어, 변수 선언은 변수 이름을 메모리 상의 주소와 연결한다.

this 바인딩은 특별한 경우로, this라는 식별자와
그것을 가리키는 객체를 연결한다.

❓Lexical Scope와의 차이

  • 함수의 Lexical Scope는 그 함수가 정의된 위치에 따라 결정되는 반면, this 바인딩은 함수가 어떻게 호출되는지에 따라 달라진다.
  • 즉, this 바인딩은 함수의 선언 위치와 상관없이,
    오로지 어디서 어떻게 함수를 호출하느냐에 따라
    결정된다고 보면 된다.

📢 함수를 호출하는 방법과 this 바인딩
1. 일반 함수 호출 (기본 바인딩)
함수가 일반적인 방식으로 호출 될 때,
this는 전역객체(브라우저는 window, Node.js에서는 global)를 가리킨다.
단 'strict mode'에서는 this가 undefined가 된다.
2. 메서드 호출 (암시적 바인딩)
객체의 메서드로서 함수가 호출되면,
this는 그 메서드를 호출한 객체를 가리킨다.
예를 들어 obj.method()에서 this는 obj를 가리킨다.
3. Function.prototype.apply/call/bind
메서드에 의한 간접 호출(명시적 바인딩)

이러한 메서드를 사용하면, 개발자가 명시적으로
this를 바인딩할 객체를 지정할 수 있다.
이 경우, this는 지정된 객체를 가리킨다.
4. 생성자 함수 호출 (new 바인딩)
new 키워드를 사용하여 함수를 생성자로 호출하면,
this는 새로운 객체를 가리킨다.

  • 🚫 예외 사항 : 'strict mode'에서는 this 바인딩이 다르게 작동한다.
    일반 함수에서 thisundefined로 설정된다.
  • 💡 실용적인 예시 : 코드 예시를 통해 각 호출 방식에서
    this가 어떻게 바인딩되는지 실제로 보여줄 수 있다.
    이를 통해 this 바인딩의 개념을 더 명확히 이해할 수 있다.
// 1. 일반 함수 호출
// 'this'는 기본적으로 전역 객체를 가리킴 (브라우저에서는 window, Node.js에서는 global)
// 'strict mode'에서는 'this'가 undefined로 설정됨
const first = function () {
console.log(this);
};
first(); // window 또는 undefined ('strict mode'에 따라 달라짐)

// 2. 메서드 호출
// 'this'는 메서드를 호출한 객체를 가리킴
const obj = { first };
obj.first(); // { first: ƒ first() }

// 3. call을 사용한 호출
// 'this'는 call의 첫 번째 인자로 명시적으로 지정된 객체를 가리킴
first.call({ a: "kim" }); // { a: 'kim' }

// 4. 생성자 함수 호출
// 'this'는 새로 생성된 인스턴스를 가리킴
new first(); // first {}

// 5. 화살표 함수
// 화살표 함수는 'this'를 자체적으로 바인딩하지 않고, 정의된 상위 스코프의 'this'를 사용함
const arrowFunc = () => {
console.log(this);
};
arrowFunc(); // 상위 스코프의 'this' (일반적으로 전역 객체 또는 'strict mode'에서는 undefined)
  • 화살표 함수는 자신의 this를 생성하지 않고, 정의된 스코프의 this를 사용한다. 이는 화살표 함수가 어휘적으로 주변 스코프(Lexical Scope)를 상속받는다는 것을 의미한다.

'this' 바인딩의 우선순위
1. new 바인딩 : new 키워드로 호출된 생성자 함수에서 this는 새로 생성된 인스턴스를 가리킨다.
2. 명시적 바인딩 : call, apply, bind를 사용할 때 this는 명시적으로 지정된 객체를 가리킨다.
3. 암시적 바인딩 : 객체의 메서드로 함수가 호출되면, this는 그 객체를 가리킨다.
4. 기본 바인딩 : 'strict mode'에서는 thisundefined가 되며
'non-strict mode'에서는 전역 객체를 가리키게 된다.

📚 참고자료

  1. 변수의 유효범위와 클로저 - Javascript.info
  2. 스코프 - poiemaweb
  3. 실행 컨텍스트의 구성 - heycoding
  4. execution_context - gamguma
  5. 모던 자바스크립트 - Deep Dive, 이웅모
profile
기억하기 보단, 기록하는 개발자로 나아가고 있습니다

0개의 댓글