본문 바로가기

자바스크립트

실행 컨텍스트(execution context) 를 다시보자

반응형

이 포스트는..

사실 많은 분들이 실행 컨텍스트에 대해서 포스트를 작성했죠. 저 또한 과거에 작성한 이력이 있고, 기술 블로그라면서 기술에 대한 포스트 보다는 이론에 대한 포스트라서 자격지심이 들기도 하지만!! 그래도 스스로의 공부를 위해서 복습하는 겸 정리해봅니다.

 

실행 컨텍스트

실행 컨텍스트란 동일한 조건이나 환경을 지니는 코드들을 실행하는 데 필요한 조건이나 환경정보를 의미한다. 쉽게 말해, 소스 코드를 실행하는데 필요한 정보를 저장하고 있는 객체이다.

 

여기서 동일한 조건이나 환경을 지니는 코드들을 하나의 덩어리로 나눠서 본다면 총 4가지로 구분 되는데, Global, Function, eval, module 으로 구분한다.

 

eval 은 보안상의 문제, 크로스 브라우징 문제 등의 이유로 사용을 추천하지 않으므로 논외로 취급하고,실행 컨텍스트에서 주요한 코드 덩어리로 볼 수 있는 것은 Global, Function, module 3 가지가 된다고 볼 수 있다.

 

전역(Global)

전역의 경우, 실행 컨텍스트를 전역 실행컨텍스트로 부르는데, 이 경우  자바스크립트 코드가 실행되는 동시에 생성된 후, 전체 소스 코드가 끝날 때 비로소 종료되는 특징을 지닌다.

 

모듈(module)

모듈의 경우에는  import 되는 즉, 모듈 내부에서 정의된 소스 코드가  다른 모듈에서 호출되거나 실행될 때 실행 컨텍스트를 생성하고, 해당 소스 코드의 사용이 종료되는 시점에 종료한다.

예를 들어,   a.js 파일 에서 export 되어 호출된 변수나 함수가 b.js 라는 모듈에서 import 되는 순간  a.js 의 모듈 실행 컨텍스트는 생성되고, 해당 변수와 함수를 사용 및 실행할 때 실행 컨텍스트도 활성화 된다. 그 후 코드 실행이 끝났다면,  모듈 실행 컨텍스트도 소멸하게 된다.

 

함수

함수의 경우도 전역과 모듈과 마찬가지로 함수를 호출할 때 함수 실행 컨텍스트가 생성 및 활성화되고, 함수 내부의 소스코드 사용이 종료됨에 따라 함수 실행 컨텍스트도 소멸하게 된다.

그럼 if/for ... 등은 실행 컨텍스트를 생성할까?
이들은 모두 블록 스코프를 형성하여 하나의 범위를 지정하지만, 실행 컨텍스트는 별도로 생성하지 않는다. 

 

실행 컨텍스트의 예시

let x = 10;

function foo() {
  let y = 20;
  console.log(x + y);
}

foo();

 

위와 같은 스크립트가 존재한다고 가정해보자. 여기서 전역 실행 컨텍스트는 위 스크립트가 활성화되는 동시에 자동으로 생성된다.

 

 foo() 함수의 경우에는 자신이 가지고 있는 지역 스코프 내부의 소스 코드를 관리한다(즉, 함수 실행 컨텍스트). 또한 실행 컨텍스트가 생성되는 시점도  foo() 함수를 호출하는 순간이 되며, 호출하지 않았다면 함수 실행 컨텍스트는 생성되지 않는다. 즉, 함수를 호출한 시점에 함수 실행컨텍스트가 생성 및 활성화되고, 스코프 내의 모든 소스코드가 실행되는 순간 함수가 종료되고 실행컨텍스트 또한 종료된다. 이러한 동작이 이루어지는 이유는 함수 실행 컨텍스트 자체가 함수 실행에 필요한 모든 정보를 담고 있는 환경 그 자체 이기 때문이다. 즉, 해당 환경에 대한 정보가 없으면 함수는 실행할 수 없다.

참고로 
실행 컨텍스트는 FIFO 를 기반으로 관리되는 콜 스택(호출 스택)과 연관된 개념이다. 스크립트가 실행되는 순간 전역 실행 컨텍스트와 생성되어 콜 스택에 쌓이고, 그 후 함수가 호출되면, 함수 실행 컨텍스트가 그 위에 쌓이게 된다. 마치 블록을 위로 한 장씩 쌓아올리는 것과 같다. 당연히 꺼낼 때도 함수 실행 컨텍스트가 제일 위에 올라가 있으므로 콜 스택에서 제거할 때도 함수 실행 컨텍스트 -> 전역 실행 컨텍스트 순으로 제거하게 된다.

 

실행 컨텍스트는 크게 변수 환경렉시컬 환경으로 구분된다. 변수 환경과 렉시컬 환경은 초기에 저장하는 정보의 형태는 같지만, 변수 환경은 한 번 저장된 값이 변경되지 않으나, 렉시컬 환경은 저장된 값이 변동되면 해당 변경에 대한 정보를 계속해서 추적하여 갱신한다. 즉, 서로 저장하는 정보는 같지만, 변동이 되느냐 되지 않느냐의 차이만 존재한다.

 

실행 컨텍스트의 구성요소: Variable Environment(변수 환경)

변수 환경은 렉시컬 환경과 마찬가지로 실행 컨텍스트를 구성하는 환경 정보를 모아서 사전 형태로 관리하고 있는 객체이다. 단, 한 번 정리된 경우에는 그 정보는 바뀌지 않는다. 스크립트의 실행과 크게 연관된 부분은 렉시컬 환경이다.

 

실행 컨텍스트의 구성요소 : Lexical Environment(렉시컬 환경)

렉시컬 환경은 실행 컨텍스트를 구성하는 환경 정보들을 모아 딕셔너리 형태로 구성한 객체이다. 즉, 특정 실행 컨텍스트에 let a = 5; 라는 변수가 존재할 때, a 는 키가 되고, 5는 해당 식별자에 담기는 값이 되는데, 렉시컬 환경은 let a : 5와 같이 마치 사전의 어휘를 수록한 목록처럼 저장하고 있다. 또한,  변수 환경과 다르게 변동사항이 생기면 해당 사항을 추적하여 기존 정보를 갱신한다.

 

렉시컬 환경은 내부적으로 두 가지 구성요소를 가지고 있는데, environment Recordouter Enviornment Reference 이다.

 

environmentRecord(환경 기록)

환경기록은  var, let, const 를 사용하여 생성된 식별자와 function a(){} 와 같은 함수 선언 등의 정보를 저장하는 객체이다. 

예를들어, var a = 1; function foo() {} 가 있다고 가정할 때, 환경 기록은 변수 선언(var a;)과 함수 선언(function(){ }) 정보를 기록하여 저장한다. 

 

환경 기록은 호이스팅(hosting) 개념과 같이 본다면 이해하기 쉬운데,  여기서 호이스팅이란 함수 선언나 변수의 선언이 스크립트의 최상위로 끌어올려지는 것을 의미한다. 이는 글로만 보면 이해가 안 되므로 변수와 함수 선언 각각을 코드로 설명해본다.

console.log(x) // undefined
var x = 10;

 

위 코드는 변수 선언문(var x) 이 최상단으로 끌어올려 지면서, x 에 undefined 가 할당 됨에 따라 생겨난 결과를 보여준다.

 

var x;
console.log(x) // undefined
x = 10;

 

이 때, 자바스크립트는 변수를 선언하고 아무런 값을 할당하지 않으면 undefined 를 기본으로 할당하는데, var 키워드로 선언된 변수 a 는 10 이라는 값을 할당받기 이전에 스크립트의 최상단으로 끌어올려 지면서 위와 같은 형태가 암묵적(보이지 않게) 이루어진다. 참고로, var 키워드로 선언된 변수의 경우 생성과 동시에 초기화가 필수적이므로 undefined 가 할당된다.

 

foo() // 호출이 된다.
function foo() {}
function foo() {}
foo()

 

함수 선언문의 경우에도 마찬가지이다. 상식적으로 생각하면, 코드는 위에서 한 줄씩 읽어서 실행되는게 맞다. 즉, 원래라면 foo() 함수가 호출될 당시에는 foo 함수가 존재하지 않기 때문에 에러가 떠야 맞지만, 함수 선언문의 경우에도 호이스팅이 이루어지면서 함수 전체가 최상단으로 선언문이 끌어올려 지게 되고, 위와 같은 결과가 암묵적으로 이루어진다.

참고로 함수 표현식(var x = function y() {}) 으로 선언된 함수의 경우에는 var x 라는 변수 선언문만 따로 최상단에 호이스팅 되고, 해당 변수에 할당된 함수는 할당될 예정인 값으로 취급하여 그 자리에 그대로 남는다.

 

 이 때 최상위로 끌어 올려진  var x 와 function foo() { }  정보를 환경 기록 객체는 저장하는 것이다.

 

이를 토대로 본다면

환경 기록은 함수 선언문이나 변수 선언 등의 현재 실행 컨텍스트 내의 식별자 정보를 기록하고 저장하고 있는 객체라고 이해할 수 있다.

 

outerEnviromentReference(외부환경참조)

외부환경참조는 현재 문맥(컨텍스트)에 관련 있는 외부 렉시컬 환경에 대한 참조 정보를 담고 있는 객체이다

렉시컬 환경은 앞서 언급한대로 함수 선언문이나 변수 선언에 대한 정보를 가지고 있는 개체이다. 즉, 외부 렉시컬 환경은 현재 렉시컬 환경의 상위에 있는 렉시컬 환경을 의미한다.

 

이 개념은 스코프 체인과 연관되어 있는데, 스코프 체인은 마치 체인이 이어진 것처럼 스코프(유효범위)가 다른 실행컨텍스트가 생성한 스코프와 체인처럼 이어져 있는 형태라고 이해할 수 있다.

예를 들어,  a 라는 변수를 실행하기 위해 a 변수가 어디에 선언되어 있는지 찾는다. 이 때 우선적으로 자신이 속한 스코프 내에서 해당 변수를 찾기 시작한다.

그런데, 현재 스코프 내에서 a 변수를 찾지 못한 경우, 외부환경참조를 통해서 상위 실행 컨텍스트의 스코프를 탐색하게 되고, 여기서도 a 를 찾지 못했다면, 또 다시 상위 실행 컨텍스트의 외부환경참조가 참조하고 있는 상위 스코프에 접근하게 된다.

이처럼 체인이 이어진 것 처럼 탐색하는 방식을 스코프 체인이라 부른다.

 

 

 

 

예를 들어, 위와  같이 전역변수와 외부함수, 내부함수가 선언되어 있다면 2와 3은 모두 렉시컬 환경을 가지고 내부적으로 외부환경참조를 가지고 있다.

이 때 외부환경참조는 자신이 선언되어 있는 실행 컨텍스트를 참조하고 있다. 예를 들어, inner 함수의 경우 outer 함수의 실행 컨텍스트 내부에서 선언되어 호출되고 있으므로 inner 함수의 외부환경참조 대상은 outer 함수의 실행 컨텍스트가 된다.

다시 말해, 외부함수 안에 내부함수가 존재하는 형태에서 내부함수의 외부환경참조는 자신을 선언하고 그 정보를 담고 있는 외부함수의 실행 컨텍스트를 참조한다라고 정리할 수 있다.

 

 

따라서 스코프 체인을 통해 자신을 호출한 상위의 실행 컨텍스트에 접근하여 x,y,z 라는 변수를 자신의 실행 컨텍스트에 가져와서 사용할 수 있게 된다.

 

그럼 여기서  한 가지를 추가적으로 이해할 수 있다. 왜 outer 함수에서는 inner 함수의 변수에 접근하지 못하는가 이다. 그 이유는 당연히 외부환경참조 대상이 자신의 하위 실행 컨텍스트를 가리키고 있지 않기 때문이다.

 

 

결론적으로,

외부환경참조는 자신을 호출하고 있는 상위 실행 컨텍스트를 가리키며(참조), 상위 스코프에 있는 변수를 가져와서 실행할 수 있도록 해준다 라고 정리할 수 있을 듯 하다.

 


(참고) 실행 컨텍스트의 구성요소

Variable Object (변수 객체)

Variable Object는 실행 컨텍스트 내에서 선언된 변수, 함수 선언 및 매개변수를 포함하는 객체이다. 
전역 컨텍스트에서는 전역 변수와 함수가 저장되며, 함수 실행 컨텍스트에서는 해당 함수의 지역 변수와 매개변수가 저장된다.

 

Scope Chain (스코프 체인)

Scope Chain은 현재 실행 컨텍스트의 Variable Object와 상위(외부) 실행 컨텍스트의 Variable Object를 연결하는 체인을 의미한다. 스코프 체인을 통해 변수를 검색할 때 현재 스코프에서부터 상위 스코프로 계단을 타듯이 올라가며 검색한다. 

 

This Value (this 값)

This Value는 현재 실행 컨텍스트에서의 this 키워드의 값을 나타낸다. this는 함수가 어떻게 호출되었느냐에 따라 다르게 결정된다. 보통 함수 표현식과 정의, 생성자 함수, 메서드로 구분되어 this가 바인딩 되는 대상이 달라진다.

 

Outer Environment Reference (외부 환경 참조)

Outer Environment Reference는 현재 실행 컨텍스트의 외부(상위) 실행 컨텍스트를 참조한다. 이를 통해 스코프 체인을 구성하고 변수 및 함수 검색을 수행한다.

 

Code (실행 코드)

Code는 현재 실행 컨텍스트에서 실행될 JavaScript 코드 블록이다. 함수 내부 또는 전역 스크립트 내에서 코드가 실행된다.

 

Lexical Environment (렉시컬 환경)

렉시컬 환경은 실행 컨텍스트의 스코프와 변수들을 관리하는 내부 구조이다. Variable Object, Scope Chain, This Value 등을 관리하며 변수 및 함수의 식별자 해결을 담당한다.

 

ExecutionContextType (실행 컨텍스트 타입)

ExecutionContextType은 현재 실행 컨텍스트의 종류를 나타낸다. 예를 들어, 전역 컨텍스트, 함수 컨텍스트, Eval 컨텍스트 등이 있다.

 

렉시컬 스코프

 함수 객체의 프로퍼티로서, 함수가 선언된 위치의 렉시컬 스코프를 참조한다. 클로저의 동작에 영향을 미치며 스코프 체인을 형성하는데 사용된다.

 

 

참고자료

 

Web: JavaScript의 실행 컨텍스트(LexicalEnvironment, Scop)

JavaScript의 동작원리를 담은 실행 컨텍스트와 그 구성에 대해 알아보자.

medium.com

 

 

this - JavaScript | MDN

JavaScript에서 함수의 this 키워드는 다른 언어와 조금 다르게 동작합니다. 또한 엄격 모드와 비엄격 모드에서도 일부 차이가 있습니다.

developer.mozilla.org

 

반응형