실행 컨텍스트와 친구들 이해하기

정주·2025년 11월 13일

CS

목록 보기
2/2
post-thumbnail

들어가며

실행 컨텍스트를 이해하기 위해서 알아야할 개념이 많이 있습니다.
근데 공부해도 뒤 돌아서면 까먹는 나란 아이.. 비정상 일까요..?
이제는 더이상 물러날 곳이 없다..!
이번에는 실행 컨텍스트를 공부하며 함께 등장했던 개념들을 차근차근 정리해보려 합니다.

실행 컨텍스트란?

자바스크립트에서 코드가 실행되는 전체적인 환경입니다.

→ 환경 :코드 실행에 영향을 주는 조건, 변수, 스코프, this 등을 의미합니다.

함수가 호출될 때 실행 컨텍스트가 생성되어 콜 스택(Call Stack)에 쌓이고, 실행 흐름이 관리됩니다.

콜 스택은 하나가 존재하고, 각 함수 호출마다 새로운 실행 컨텍스트가 생성되어 기존 콜 스택에 푸시(push) 콜 스택에 쌓입니다.

콜 스택은 실행 순서를 관리하며, 컨텍스트가 끝나면 스택에서 팝(pop)됩니다.

  • 전역 실행 컨텍스트
    • 자바 스크립트가 처음 실행될 때 생성되는 컨텍스트입니다.
    • 프로그램이 종료될 때까지 유지합니다.
    • 전역에 선언된 변수, 함수가 포함되었기 때문에 어디서든 접근 가능합니다.
    • 기본적으로 자바스크립트는 싱글 스레드이기 때문에 전역 실행 컨텍스트 1개만 존재합니다.
  • 함수 실행 컨텍스트
    • 함수가 호출될 때마다 생성되는 컨텍스트로, 각 함수마다 자신만의 실행컨텍스트 보유합니다.
    • 함수 실행 컨텍스트 내에서 선언된 변수와 함수는 그 함수 안에서만 새롭게 생성되고 유효하며, 함수 내부에서는 자신의 컨텍스트 + 외부 스코프(상위 스코프)의 변수에도 접근할 수 있습니다.
    • 함수가 종료되면 실행 컨텍스트도 함께 사라집니다.

실행 컨텍스트 중요 구성요소

변수 환경 Variable Environment

  • 실행 컨텍스트 내에서 사용되는 변수와 함수 선언을 저장하는 초기 환경입니다.
    ⇒ 실행 컨텍스트가 생성될 때 var, 함수 선언문은 먼저 변수 환경에 등록되기 때문에
    var함수 선언문은 호이스팅이 빠르게 일어납니다.
  • 변수 환경은 실행 컨텍스트 생성 시 한 번 만들어지고, 이후 실행 단계에서는 렉시컬 환경에서 실제 변수 값을 관리합니다.
    ⇒ 즉, 변수 환경 자체는 업데이트되지 않습니다.

렉시컬 환경 Lexical Environment

  • 환경 레코드 Environment Record

    • 현재 스코프에서 선언된 변수(let, const), 함수 표현식, 매개변수 등이 실제로 저장되는 객체입니다.
    • 실행 중 값이 바뀌면 이 환경 레코드가 업데이트되어 변수의 최신 상태를 관리합니다.
    • var, 함수 선언문도 초기 선언 단계 이후에는 결국 이 환경 레코드에서 관리됩니다.
  • 외부 환경 참조 Outer Environment Reference

    • 현재 렉시컬 환경이 어떤 외부 스코프와 연결되어 있는지를 나타내는 참조입니다.
      ⇒ 이를 통해 스코프 체인(Scope Chain)이 만들어지고, 자바스크립트가 변수를 찾을 때
      “현재 → 부모 → 그 부모 …” 순서로 탐색할 수 있습니다.
  • 렉시컬 환경은 코드 실행 중 계속 변하는 활성 상태의 환경입니다.

  • let, constTDZ(Temporal Dead Zone) 상태도 이 환경 레코드에서 관리합니다.
    ⇒ 실행 중 변수가 업데이트되면 모두 렉시컬 환경에서 처리합니다.

디스 바인딩 (this binding)

  • this는 실행 컨텍스트에 따라 참조하는 대상이 달라지는 동적 바인딩입니다.
    렉시컬 환경은 “코드가 어디에서 작성되었는지”로 결정되는 반면,
    this는 “함수가 어떻게 호출되었는지”에 의해 결정되기 때문입니다.
    즉, 동일한 위치에 정의된 함수도 호출 방식이 달라지면 this가 달라질 수 있습니다.

  • 전역 컨텍스트는 2가지 환경 기록을 가집니다.

    • Object Environment Record
      • 전역 객체(window, globalThis 등)를 기반으로 var, 함수 선언문이 여기에 바인딩 됩니다.
        그래서 var a = 1를 작성하면, window.a로 접근이 가능합니다.
    • Declarative Environment Record
      • 전역 스코프에 생성되는 독립적인 환경으로 let , const 변수가 여기에 저장됩니다.
        그래서 var a = 1를 작성하면, window.a로 접근이 불가능합니다.
  • 함수 컨텍스트에서 this는 함수 호출 방식에 따라 달라집니다.

    1. 일반 함수 호출
      func()

      • 기본적으로 this는 전역 객체입니다. 그러나 strict 모드에서는 thisundefined가 됩니다.
    2. 객체의 메서드로 호출
      obj.method()

      • this는 해당 객체(obj)가 됩니다.
    3. 생성자 함수 호출
      new Person()

      • this는 새로 만들어진 객체가 됩니다.
    4. 화살표 함수
      () => {}

      • 속해 있는 외부 스코프의 this를 그대로 가져오기 때문에 새로 만들지 않습니다.
        예를 들어, 아래의 코드가 있으면 thisobj가 됩니다.
      const obj = {
        name: "Alice",
        greet: function() {
          console.log(this.name);
        }
      };
      
      obj.greet(); // Alice
    • 화살표 함수는 렉시컬 스코프를 따르기 때문에 this가 상위 컨텍스트를 참조합니다.
      const obj = {
          name: 'JavaScript',
          getName: function() {
              return this.name;
          }
      };
      console.log(obj.getName()); // 'JavaScript'
      const getName = obj.getName;
      console.log(getName()); // undefined => Why? 일반 함수는 기본적으로 전역 객체를 this로 참조 

왜 화살표 함수의 this만 상위 스코프를 가져오는가?
일반 함수는 호출될 때마다 this가 새로 만들지만, 화살표 함수는 this를 새로 만들지 않습니다.
왜냐하면 화살표 함수는 콜백, 이벤트, 비동기 코드에서 this가 혼동되는 문제를 해결하기 위해 만들어졌기 때문입니다.

자바스크립트 실행과정

  • 전역 실행 컨텍스트를 생성하여 콜스택에 푸쉬합니다.
  • 전체 코드 스캔 후, 선언할 변수, 함수등이 있는지 확인합니다.
  • 선언문만 실행해서 환경 레코드에 기록하며 호이스팅이 이뤄집니다.

    호이스팅이란 변수와 함수 선언이 코드의 최상단으로 끌어올려지는 현상입니다.
    자바스크립트 엔진은 실행 컨텍스트를 생성할 때 소스 코드를 먼저 스캔하여 변수 및 함수 선언을 수집하고,
    환경 레코드(Environment Record)에 등록하기 때문에 발생합니다.

  • 환경 레코드에 새로운 식별자를 기록합니다.
    • varundefined로 초기화 후 변수 환경에 등록됩니다.
    • let, const → 렉시컬 환경에 선언만 등록되고 초기화 전까지 TDZ상태가 됩니다.
    • 함수 선언문 → 함수 객체 자체로 즉시 초기화되어 등록됩니다.

  • 선언문 외 나머지 코드를 순차적으로 실행합니다.
  • 변수 참조 시, 렉시컬 환경에서 탐색합니다.
    ⇒ 현재 환경에 없으면, 외부환경참조(Outer)를 따라 상위 스코프로 이동합니다.
    • 이 과정에서 아래의 동작들이 발생합니다.
      • 식별자 결정
        • 코드에서 사용된 변수나 함수 이름(식별자)이 어떤 값을 참조할지 결정하는 과정입니다.
          ⇒”이 this는 어떤 스코프의 this인가?"를 찾는 단계입니다.
      • 스코프 체인(Scope Chain)
        • 식별자를 결정할 때, 현재 렉시컬 환경에서 시작해 외부 환경으로 이어지는 연결 구조입니다.
        • “현재 → 부모 → 조상 환경” 순으로 탐색합니다.
      • 변수 쉐도잉(Vairable Shadowing)
        • 하위 스코프에서 상위 스코프와 같은 이름의 변수를 선언해, 상위 변수의 접근이 가려지는 현상입니다.

호이스팅과 실행 컨텍스트 과정

변수 호이스팅

  • var 선언
console.log(hoisting); // undefined
var hoisting = '호이스팅';
console.log(hoisting); // 호이스팅
  • 변수 hoisting는 선언 전에 끌어올려져 첫 번째 console.log에서 접근 가능합니다

    • 선언과 초기화가 동시에!
      • 선언 : 메모리 공간을 확보하고 식별자와 연결
      • 초기화 : 식별자를 암묵적으로 undefined 값 바인딩
  • let, const

         console.log(hoisting); // Reference Error
         const hoisting = '호이스팅';

    • 자바스크립트 엔진이 변수를 선언하지만 값을 초기화하지는 않습니다.
      let, const로 선언된 변수는 Temporal Dead Zone(TDZ)에 들어가며, 초기화 전에는 접근이 불가능합니다.
    • 일시적 사각지대 Temporal Dead Zone : let, const로 선언했을 때, 선언 이전에 식별자를 참조할 수 없는 구역

함수 호이스팅

  • 함수 선언

       /* Global */
       study()  // TypeError: 존재하지만 함수가 아닐 때
       
       var study = () =>{
       	// do study
       };
    • var로 선언했기 때문에 studyundefined입니다.
    • 함수와 달리 호출될 수 없기 때문에 타입 에러 발생합니다.
       /* Global */
       study() // Reference Error :변수가 존재하지 않을 때
       
       const study = () =>{
       	// do study
       };
    • const로 선언시, 환경레코드에 기록된 값이 없어 Reference Error 발생
  • 함수 표현
    ⇒ 함수 표현식 : 변수에 함수를 담아서 함수를 선언하는 방식

    → 함수를 변수에 담고 있기 때문에 변수 호이스팅과 똑같이 동작합니다.

          /* Global */
          study() // 실행함 
          
          function study(){
          	// do study
          };
    • 자바스크립트 엔진이 study 함수를 선언과 동시에 완성된 함수 객체를 생성해 환경레코드에 기록합니다.
      ⇒ 함수 선언과 동시에 함수가 생성되기 때문에 선언 전에도 함수를 사용할 수 있습니다.

그럼 비동기 함수는 어떻게 실행되는가?

동기 함수는 실행되면 → 실행 컨텍스트 생성 → 콜 스택 쌓임 → 코드 실행 → 컨텍스트 제거 됩니다.
비동기 함수는 어떻게 실행컨텍스트가 실행되는걸까요?

  • 비동기 함수 실행과정
  1. 콜백 큐에 등록됩니다.
    콜백 큐는 비동기 함수의 콜백들이 대기하는 공간으로 타이머, 이벤트 등 실행 준비가 된 함수들이 이곳에 들어갑니다.

  2. 이벤트 루프(Event Loop)를 통해 콜백 함수가 실행 컨텍스트 콜 스택에 올라가고 실행 컨텍스트가 생성됩니다.
    이벤트 루프는 콜 스택이 비었는지 계속 확인하며 콜 스택이 비어 있으면 콜백 큐에서 함수를 하나씩 꺼내 콜 스택에 올립니다.

  3. 실행 컨텍스트가 생성되어 함수가 실행됩니다.
    비동기 이벤트 발생 → 콜백 큐 등록 → 콜 스택 비면 이벤트 루프가 콜백 스택에 올림 → 실행 컨텍스트 생성

  • 예시
setTimeout(() => {
    console.log('Hello, World!');
}, 1000);
console.log('Synchronous Code');
  1. setTimeout 호출 → 브라우저/Node.js가 타이머를 등록

  2. 콜백 함수(() => console.log('Hello, World!'))는 즉시 실행되지 않고
    콜백 큐(Callback Queue)에 등록

  3. 현재 실행 컨텍스트(전역 컨텍스트)는 동기 코드 실행 후 종료 → 콜스택 비워짐

  4. 이벤트 루프(Event Loop)가 콜백 큐를 확인 → 콜백을 콜스택으로 올려서 실행 컨텍스트 생성 → 실행

실행 컨텍스트와 클로저의 관계

클로저란?

  • 함수가 자신이 선언될 때의 렉시컬 환경을 기억하는 것으로, 함수가 실행된 이후에도 외부 변수에 접근 가능 합니다.

  • 자바스크립트에서는 함수가 생성될때마다 클로저가 생성됩니다. 각각의 함수가 자신만의 클로저를 갖습니다.

  • 실행 컨텍스트가 종료된 이후에도 변수에 접근 가능합니다.

  • 예시코드

    function outer() {
        let count = 0;
        return function inner() {
            count++;
            return count;
        };
    }
    const counter = outer();
    console.log(counter()); // 1
    console.log(counter()); // 2
    • 클로저 덕분에 inner 함수는 outer 함수의 실행 컨텍스트가 종료된 이후에도 count 변수에 접근 가능합니다.
    • 클로저는 자바스크립트에서 상태를 유지하거나 캡슐화 구현하는데 자주 사용합니다.

📢 실행 컨텍스트에 대해서 설명해주세요!

실행 컨텍스트는 자바스크립트가 코드를 실행할 때 변수, 함수 선언, this, 스코프 등의 정보를 관리하는 환경 객체입니다. 전역 코드가 실행될 때 전역 실행 컨텍스트가 생성되고, 함수가 호출될 때마다 새로운 함수 실행 컨텍스트가 만들어집니다. 이 컨텍스트들은 콜 스택을 통해 순서대로 실행되며, 실행 흐름을 관리합니다. 실행 컨텍스트는 변수 환경, 렉시컬 환경, 그리고 this 바인딩으로 구성되어 있어, 변수 호이스팅, 스코프 체인, TDZ와 같은 규칙을 관리하고, this는 호출 방식에 따라 달라집니다. 자바스크립트 엔진은 이러한 정보를 활용해 변수를 어디서 찾고 어떤 순서로 코드를 실행할지 결정합니다.

마치며

최대한 시각화하려고 했는데요 ㅠㅠ
이 친구들,,, 생각보다 호락호락하지 않군여..
이벤트 루프와 비동기를 좀 더 보충해서 공부해야될 것 같습니다.
그래도 실행컨텍스트의 개념을 한 번 정리해두니 이제 흐름이 보이는 것 같습니다!
이번엔 머릿속에 조금 더 오래 남을 것 같아요..ㅎ


참고자료

https://www.youtube.com/watch?v=EWfujNzSUmw

https://f-lab.kr/insight/understanding-javascript-execution-context-and-closure-20250124?gad_source=1&gad_campaignid=22368870602&gbraid=0AAAAACGgUFe0AKwxSBXcjjQBLo3zoDW6t&gclid=CjwKCAiA_dDIBhB6EiwAvzc1cObvuium3wyMKurFquBwwTxHlfyGKUXZBTg6-Kt-Pi67jCOpcX8ZURoC1vkQAvD_BwE

https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Closures

profile
💡프론트엔드 공부 기록

0개의 댓글