JavaScript 키워드 실행 컨텍스트 Part 1

🤔 왜 실행 컨텍스트를 알아보고 싶었는가?

앞전에 여러 키워드를 알아보면서 항상 나오는 이야기는

'실행 컨텍스트를 학습하면 더 이해를 할 수 있다.'

이런 이야기가 나왔었다. 그만큼 자바스크립트의 동작 원리의 핵심이라는 생각이 들었고, 내가 자바스크립트로 밥을 벌어먹고 사는 사람이 되려면 이 개념은 알아봐야겠다 라는 생각을 했다.

실행 컨텍스트를 학습하는데 있어서 이용한 레퍼런스는 모던 자바스크립트 Deep Dive라는 책과 부스트캠프 7기의 백엔드 아버지 황준일님의 글을 참고하면서 학습을 진행했다.

그리고 앞전에 쓴 프로토타입 글이 너무 상세한 내용을 담고 있어서 '이게 정리하는 맞는 방법인가?' 라는 생각이 조금 들어서 이번 키워드는 조금 더 정재해서 글을 써보려 한다.



⚙️ 실행 컨텍스트란 무엇일까?

용어에 대해 알아보자

실행 컨텍스트라는 것이 무엇일까? 모던 자바스크립트 Deep Dive책에서는 다음과 같이 이야기 한다.

실행 컨텍스트는 소스코드를 실행하는 데 필요한 환경을 제공하고 코드의 실행 결과를 실제로 관리하는 영역이다.
좀 더 구체적으로 말해, 실행 컨텍스트는 식별자(변수, 함수, 클래스 등의 이름)를 등록하고 관리하는 스코프와 실행 순서 관리를 구현한 내부 메커니즘으로, 모든 코드는 실행 컨텍스트를 통해 실행되고 관리된다.

한마디로 우리가 작성한 자바스크립트 코드를 실행하고 관리하는 내부 메커니즘이라고 볼 수 있다.

우선 그러면 우리가 열심히 작성한 코드들이 어떻게 흘러가면서 실행이 되는지 해당 사이클을 알아보자

소스코드의 평가와 실행

모든 소스코드는 실행하기 전 평가 과정을 거친다. 이는 우리가 호이스팅이라는 개념을 배우면서 경험해봤다.

평가 과정에서는 실행 컨텍스트를 생성하고 변수, 함수등의 선언문만 먼저 실행해서 변수나 함수 식별자를 키로 실행 컨텍스트가 관리하는 스코프에 등록한다.

평가 과정이 끝나면 미리 처리를 끝낸 선언문을 제외한 다른 소스코드들을 순차적으로 실행한다. 이때 소스코드 실행에 필요한 정보를 실행 컨텍스트가 관리하는 스코프에서 검색하고 찾아서 사용한다.

이후 변수 값의 변경이나 소스코드의 실행 결과를 다시 실행 컨텍스트가 관리하는 스코프에서 등록을 한다.

이러한 과정을 거치면서 우리가 작성한 코드들이 흘러가면서 내부에서 뚝딱 뚝딱 작업이 이루어진다.

실행 컨텍스트의 역할

지금 보면 실행 컨텍스트가 변수나 함수들을 저장하고 관리하는 역할인 것 같은데 정확하게 이 실행 컨텍스트가 어떠한 역할을 담당하는지 알아보자.

const x = 1;
const y = 1;

function foo(a) {
  const x = 10;
  const y = 20;

  console.log(a + x + y);
}

foo(100);

console.log(x + y);

이 코드를 예시로 위에서 알아본 평가와 실행의 흐름을 알아보자

  1. 먼저 전역 코드가 평가된다. 맞다. 호이스팅이다. 전역 코드의 변수 선언문과 함수 선언문이 먼저 실행되고, 그 결과 생성된 전역 변수와 전역 함수가 실행 컨텍스트에 등록될 것이다.

  2. 전역 코드 실행 이제 선언문을 제외한 나머지 코드들이 순서대로 진행된다. 진행되다가 함수 foo를 만나고 함수 내부로 이동한다.

  3. 함수 코드 평가 함수로 이동하게 되면 앞전과 동일하게 평가 과정을 거치며 매개변수, 지역 변수 선언문등이 먼저 실행된다. 이후 결과로 생성된 매개변수와 지역 변수는 실행 컨텍스트에 저장된다. 또한 함수 내부에서 지역 변수처럼 사용할 수 있는 arguments 객체가 생성되어 지역 스코프에 등록되고 this 바인딩도 결정된다.

  4. 함수 코드 실행 동일하게 선언문 제외 코드들이 실행되고 이제 console.log 메서드가 호출된다. console은 어디있을까? 바로 전역 객체의 프로퍼티에 존재한다. 이후 log 프로퍼티를 검색하고 인수에 a + x + y가 평가되고 전달되어야 한다. 이때 a, x, y는 스코프 체인을 통해서 검색된다.

  5. 다시 전역 코드 실행 함수 실행이 종료된 이후 전역에 있는 코드들이 동작한다.

이러한 흐름으로 진행이 이루어진다. 이 과정에서 실행 컨텍스트는 어떠한 도움 혹은 역할을 했을까?

  • 선언에 의한 식별자를 스코프를 구분해서 등록하고 관리해주고 있다.
  • 중첩 관계에 의해 스코프 체인을 형성해주고 있다.
  • 현재 실행 중인 코드의 실행 순서를 변경하고 이동시켜주고 있다.

크게 식별자, 스코프, 코드 실행 순서 이렇게 3가지를 관리해주는 역할이다. 실행 컨텍스트는 식별자와 스코프는 렉시컬 환경으로, 코드 실행 순서는 실행 컨텍스트 스택으로 관리가 된다. 이 두 가지를 알아보자.

실행 컨텍스트 스택

실행 컨텍스트는 코드 실행 순서를 담당한다. 자바스크립트 엔진은 먼저 전역 코드를 평가하여 전역 실행 컨텍스트를 생성한다. 그리고 함수가 호출되면 함수 코드를 평가하여 함수 실행 컨택스트를 생성한다. 이때 생성된 실행 컨텍스트는 스택 구조로 관리가 되고 이를 실행 컨텍스트 스택이라고 한다.

const x = 1;

function foo() {
  const y = 2;

  function bar() {
    const z = 3;
    console.log(x + y + z);
  }

  bar();
}

foo();

위 코드는 다음과 같이 실행 컨텍스트 스택이 추가되고 제거된다.

  1. 먼저 전역코드의 평가가 일어나고 실행 컨텍스트를 생성하고 스택에 추가된다.

  2. 이후 foo가 호출이 되면 전역 코드의 실행은 일시 중단되고 코드의 제어권이 foo 함수 내부로 이동된다. 자바스크립트 엔진은 foo 함수 내부 코드를 평가하고 foo 함수의 실행 컨텍스트를 생성하고 스택에 추가한다.

  3. 중첩함수 bar가 호출되면 foo 함수 코드의 실행은 일시 중단되고 코드의 제어권이 bar 함수 내부로 이동된다. 동일하게 평가 및 실행 컨텍스트 생성 및 스택 추가가 일어난다.

  4. bar 함수의 실행이 console.log 메서드까지 마치고 종료되면 제어권이 다시 foo 함수로 이동한다. 이후 실행 컨텍스트 스택에서 제거된다.

  5. foo 함수가 종료되면 제어권이 전역으로 이동하고 실행 컨텍스트 스택에서 제거된다.

이렇게 실행 컨텍스트 스택이 관리된다. 한 가지 알아가면 좋은 것은 실행 컨텍스트 스택의 최상위에 존재하는 실행 컨텍스트는 언제나 현재 실행 중인 코드의 실행 컨텍스트라는 점이다. 이렇게 최상단에 있는 실행 컨텍스트를 실행 중인 실행 컨텍스트(running execution context) 라고 한다.

렉시컬 환경

렉시컬 환경은 식별자와 식별자에 바인딩된 값, 그리고 상위 스코프에 대한 참조를 기록하는 자료구조이다. 앞서 말했던 것 처럼 렉시컬 환경은 스코프와 식별자를 관리한다.

렉시컬 환경은 키와 값을 갖는 객체 형태의 스코프(전역, 함수, 블록)를 생성하여 식별자를 키로 등록하고 식별자에 바인딩 된 값을 관리한다. 즉 렉시컬 환경은 스코프르 구분하여 식별자를 등록하고 관리하는 저장소 역할을 하는 렉시컬 스코프의 실체이다.

생성 초기의 실행 컨텍스트와 렉시컬 환경은 아래와 같다. 같이 확인하고 용어를 정리해보도록 한다.

실행 컨텍스트는 Lexical Environment와 Variable Environment 두 컴포넌트로 이루어져있다.

  • Lexical Environment(렉시컬 환경)

    • 초기에는 Variable Environment와 같은 렉시컬 환경을 참조한다.

    • 변경사항이 실시간으로 반영이 된다.

    • 내부는 환경 레코드(Environment Recode)와 외부 렉시컬 환경에 대한 참조(Outer Lexical Environment Reference)라는 컴포넌트로 구성되어 있다.

  • Variable Environment

    • 최초 실행 시의 스냅샷을 유지한다.

    • 변경사항이 반영되지 않는다.

    • 실행 컨텍스트를 생성할 때 Variable Environment에 먼저 정보를 담고, 이를 복사해서 Lexical Environment를 만든다.

    • 주로 Lexical Environment를 이용하고 Variable Environment는 스냅샷 저장용이다. 이로 인해서 앞으로 진행하면서 Variable Environment는 생략할 예정이다.

  • 환경 레코드(Environment Recode)

    • 스코프에 포함된 식별자를 등록하고 등록된 식별자에 바인딩된 값을 관리하는 저장소이다.

    • 소스코드 타입에 따라 관리하는 내용에 차이가 있다.

    • 호이스팅이 발생하는 이유이다.

  • 외부 렉시컬 환경에 대한 참조(Outer Lexical Environment Reference)

    • 외부 렉시컬 환경에 대한 참조는 상위 스코프를 가리킨다.

    • 외부 렉시컬 환경에 대한 참조를 통해 단방향 링크드 리스트인 스코프 체인을 구현한다.



😃 다음에 이어질 내용

우선 실행 컨텍스트란 무엇인지, 어떠한 역할을 하는지, 그리고 어떠한 구조로 되어있는지를 살펴보았다.

다음에 이야기할 내용은 예제 코드를 가지고 어떠한 순서대로 내부 구조가 생성되는지를 그림과 함께 알아보려 한다.

나도 모르는 사이에 자바스크립트 엔진이 내부적으로 처리해주는 것이 많다는 것을 느꼈고, 스코프에 대한 이해도가 조금 더 높아질 수 있을 것 같다는 생각이 들었다.