카테고리 없음

자바스크립트 실행 컨텍스트란? (Javascript Execution Context)

Junseub 2024. 5. 29. 02:14

자바스크립트의 실행 컨텍스트(Execution Context)는 자바스크립트의 동작 원리와 관련된 핵심 개념입니다. 실행 컨텍스트가 어떻게 동작하는지 이해하면 호이스팅의 발생 이유, this 값의 설정 방식, 클로저의 동작 원리 등을 이해할 수 있습니다. 

 

실행 컨텍스트란?

실행 컨텍스트란 코드가 실행될 때 접근 가능한 식별자에 바인딩된 변수/함수 값, this 객체, 상위 스코프에 대한 참조 등의 정보를 의미합니다. 예를 들어 어떤 함수 내에서 변수 a를 접근할 때 이 a라는 식별자에 어떤 값이 바인딩 되었는지를 어디에다가 저장해두고 필요할 때 접근이 가능해야 할텐데, 이를 가능하게 하는 것이 바로 실행 컨텍스트 객체인 것입니다. 그럼 이 저장하는 행위는 언제 하게 되는걸까요?

 

코드의 실행 단계에 대해서

우리가 코드를 실행할 때 단순히 위에서 아래로 쭉 읽으면서 코드가 실행되고, 함수를 발견하면 함수로 이동할 것이라고 생각할 수 있지만, 사실은 1) 소스 코드 평가, 2) 소스 코드 실행 이렇게 두가지 과정으로 나뉩니다. 그리고 실행 컨텍스트는 바로 소스 코드 평가 단계에서 생성됩니다. 소스 코드 평가 과정에서는 변수나, 함수의 선언문 들은 먼저 실행하여 생성된 변수나 함수를 실행 컨텍스트의 환경 레코드(아래에서 자세히 다룹니다)에 등록하게 됩니다. 그리고 소스 코드 평가가 끝이 나면 비로소 소스 코드의 실행이 이루어지면서 나머지 코드가 순차적으로 실행됩니다. 

동일한 소스 코드에 대해 평가, 실행이 나누져서 이뤄집니다.

 

위의 소스 코드 평가 단계를 보면 호이스팅이 이뤄지는 이유를 알 수 있습니다. 호이스팅이란 변수나 함수 선언문이 위로 끌어 올려지는 것을 의미하는데, 실제로는 위로 끌여 올려진다기보다는 선언문을 먼저 실행하게 되는 것입니다. 따라서 변수에 접근하는 코드가 변수의 선언문보다 위에 있더라도 참조 에러 없이 정상적으로 동작하는 것입니다

실행 컨텍스트의 구조

일반적으로 실행 컨텍스트는 LexicalEnvironment 컴포넌트와 VariableEnvironment 컴포넌트로 구성됩니다. 이 둘은 처음 생성시 Lexical Environment라고 하는 렉시컬 환경 객체를 참조합니다. 이후 strict mode, eval code, try/catch 등의 상황에서 VariableEnvironment는 별개의 렉시컬 환경 객체를 생성하여 참조하게 됩니다. 그러나 일반적으로 둘은 동일한 렉시컬 환경 객체를 참조한다 라고 이해해도 좋습니다. 따라서 우리는 LexicalEnvironment 컴포넌트에 대해서만 알아보도록 하겠습니다.

실행 컨텍스트(Execution Context)의 LexicalEnvironment 와 VariableEnvironment는 모두 Lexical Environment를 가르킨다.

 

렉시컬 환경 (Lexical Environment)

렉시컬 환경은 식별자와 식별자에 바인딩된 값을 관리하는 환경 레코드(EnvironmentRecord) 그리고 상위 스코프에 대한 참조를 가리키는 외부 렉시컬 환경에 대한 참조(OuterLexcialEnvironmentReference) 컴포넌트로 구성됩니다. 예를 들어 abc, def라는 변수가 있는 실행 컨텍스트의 렉시컬 환경은 다음처럼 생성됩니다.

Lexical Environment는 환경 레코드, 외부 렉시컬 환경에 대한 참조로 구성된다.

 

다만 실행 컨텍스트는 전역, 함수 이렇게 두가지로 구분이 되는데요, 둘의 실행 컨텍스트 구조는 약간 다릅니다.

 

전역 실행 컨텍스트

전역 실행 컨텍스트는 전역 코드를 평가하는 과정에서 생성됩니다. 이 때 전역 스코프에 있는 변수와 함수 선언문이 실행되어 식별자와 값이 바인딩되어 전역 스코프에 등록됩니다. 전역 실행컨텍의 환경 레코드는 객체 환경 레코드, 선언적 환경 레코드 그리고 전역 this 바인딩으로 구성되어 있습니다.

 

객체 환경 레코드는 var 키워드로 선언된 변수와 함수 선언문으로 선언된 함수, 그리고 빌트인 전역 프로퍼티와 함수등을 관리합니다. 정확히는 BindingObject 라고 부르는 객체와 연결이 되는데, BindingObject는 전역 코드가 평가되기 이전에 생성되는 전역 객체를 의미합니다. 전역 객체는 빌트인 전역 프로퍼티와 빌트인 전역 함수, 그리고 표준 빌트인 객체가 추가되며, 동작 환경에 따라 여러 호스트 객체를 포함하는 개체를 의미합니다.(일반적으로 브라우저의 window를 생각하시면 됩니다.) var로 선언한 변수들을 전역 객체의 프로퍼티로 접근 가능한 것도 바로 객체 환경 레코드가 전역 객체로 연결되어 있기 때문입니다.

 

선언적 환경 레코드는 let, const 키워드로 선언된 전역 변수들을 관리합니다. ES6의 let, const 키워드로 선언한 변수들은 전역 객체의 프로퍼티가 되지 않는데, 이는 바로 이러한 변수들이 객체 환경 레코드가 연결되 전역 객체의 프로퍼티가 되는 것이 아니라, 별개의 환경 레코드에 저장되기 때문입니다.

 

전역 this 바인딩은 위에서 설명한 전역 객체를 가르킵니다.

 

예를 들어, 다음의 코드가 있다면 그림과 같이 전역 실행 컨텍스트가 생성됩니다.(전역 실행 컨텍스트이기 때문에 외부 렉시컬 환경에 대한 참조는 null입니다.

var x = 10;
let y = 20;

function outer () {
 ...

전역 실행 컨텍스트

 

 

함수 실행 컨텍스트

함수 실행 컨텍스트는 함수 호출에 의해 함수 코드가 실행되기 앞서 함수 코드를 평가하는 과정에서 생성됩니다. 이 때 매개변수와 지역 변수 선언문이 실행되어 식별자에 변수 값이 바인딩되어 함수 지역 스코프에 등록됩니다. 또한 this 및 외부 실행 컨텍스트에 대한 참조도 결정됩니다.

 

함수는 호출될때 코드 평가가 이루어지고, 이때 실행 컨텍스트가 생성이 됩니다. 함수 실행 컨텍스트의 렉시컬 환경도 마찬가지로 함수 환경 레코드와 외부 렉시컬 환경에 대한 참조로 이루어져있는데요, 함수 환경 레코드의 구조가 다릅니다.

 

함수 환경 레코드는 전역 환경 레코드와 다르게 하나로 되어 있는데요, var, let으로 선언된 모든 변수화 함수들을 하나의 환경 레코드에서 등록하고 관리합니다. 또한, 매개변수, arguments 객체 등도 관리합니다.

 

함수 환경 레코드의 ThisValue에도 this가 바인딩되는데요, 일반적으로 this는 전역 객체를 가르킵니다.

 

예를 들어, 다음의 코드가 있다면 그림과 같이 전역 실행 컨텍스트와 함수 실행 컨텍스트가 생성됩니다.

var x = 10;
let y = 20;

function outer (a) {
  var x = 1;
  let y = 2;
  return x + y; 
}

 

함수 실행 컨텍스트와 전역 실행 컨텍스트

실행 컨텍스트의 용도

지금까지 글을 보다보면 그래서 실행 컨텍스트가 왜 존재하는지, 왜 외부 렉시컬 환경에 대해서 참조를 가지는 지에 대해서 의문을 가질 수도 있습니다. 이는 바로 식별자 결정을 위해서입니다. 식별자 결정은 동일한 이름의 식별자가 다른 스코프에 여러 존재한다면, 가장 가까운 스코프의 식별자를 참조한다는 것입니다. 예를 들어 위의 예제 코드에서 outer 함수의 실행 결과값은 무엇일까요? 정답은 3입니다. 이는 x, y라는 식별자에 등록된 값들중 가장 가까운 값이 바로 outer 렉시컬 환경에 있는 1, 2이기 때문입니다. 만약 outer 함수에 x, y 선언문이 없었다면 가장 가까운 다음 스코프인 전역 스코프에서 x, y 값으로 결정되어 반환값은 30이 될 것입니다.

 

실행 컨텍스트 스택

마지막으로 실행 컨텍스트는 어떤식으로 관리되는지에 대해서 알아보겠습니다. 실행 컨텍스트는 스택 자료 구조로 관리가 됩니다. 즉, 코드 평가가 이루어져 실행 컨텍스트가 생성되면 실행 컨텍스트 스택에 최상위에 올라가게 되고, 코드가 실행이 끝나면 실행 컨텍스트 객체가 스택에서 제거됩니다. 실행 컨텍스트 스택에 최하단에는 당연히 전역 실행 컨텍스트가 존재하게 됩니다. 그리고 어떤 함수 실행문은 만나게 되면 함수 실행 컨텍스트를 생성하여 컨텍스트 스택에 올리게 됩니다. 실행이 종료되면 실행 컨텍스트 스택에서 제거됩니다.

 

 

마무리

오늘은 실행 컨텍스트가 어떻게 형성이 되어있는지, 어떻게 사용되는지, 또 어떻게 관리되는지에 대해서 알아보았습니다. 실행 컨텍스트는 자바스크립트의 호이스팅, 스코프, 클로저 등 여러 가지 특징을 이해하는데 매우 중요한 요소입니다. 실행 컨텍스트를 잘 이해하며 자바스크립트를 더욱 잘 다루는 좋은 개발자가 될 수 있다고 생각합니다. 읽어주셔서 감사합니다.