일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- Vite
- dynamic import
- React Query
- 개발자
- http
- html
- 백준
- TypeScript
- 프론트엔드
- 코딩테스트
- JavaScript
- React
- Next.js
- 취업
- csr
- Browser
- error
- 차이
- css
- 에러
- 알고리즘
- SSR
- 비동기
- 취업준비
- DOM
- 공부
- 코딩
- Git
- 자바스크립트
- Sass
- Today
- Total
minTech
[JavaScript] 실행 컨텍스트 (Execution Context) 본문
javascript를 공부하다보면 다른 파트이더라도 반복되는 개념들이 많이 등장한다.실제로 자바스크립트를 공부하며 반복되는 단어 중 "실행 컨텍스트" 라는 말도 정말 자주 들어보았다. 그래서 이 단어에 대해 완벽하게 머릿 속에서 정리하고자 글을 작성해보았다.
실행 컨텍스트의 개념은 자바스크립트에서 굉장히 중요하고 핵심적인 개념이다. 이 실행 컨텍스트에 대해 완벽하게 이해한다면, 소스코드를 실행할 때 식별자와 변수를 관리하는 방식과 호이스팅이 발생하는 이유에 대해 자연스럽게 이해할 수 있다.
👀 들어가기 전에
자바스크립트 엔진은 소스 코드를 실행하기 위해 2가지 단계를 수행한다.
1) 소스코드를 평가한다.
실행 컨텍스트를 생성하고, 선언문만 실행하여 생성된 변수나 식별자를 실행 컨텍스트가 관리하는 스코프에 등록한다.
2) 소스코드를 실행한다.
소스코드의 실행에 필요한 정보, 변수나 함수를 실행 컨텍스트에서 관리하는 스코프에서 검색하여 바인딩된 값을 찾는다. 소스코드의 실행 후 그 결과는 다시 스코프에 등록한다
📚 실행 컨텍스트란 무엇일까?
실행 컨텍스트란 소스코드를 실행하는데 필요한 환경을 제공하고,
코드의 실행 결과를 실제로 관리하는 영역이다.
소스코드를 실행할 때, 필요한 환경을 제공하는 것이 무엇일까?
1. 다른 스코프에서는 동일한 이름으로 변수의 선언이 가능하다.
➡️ 식별자와 바인딩된 값은 스코프를 구분하여 관리되어야한다.
2. 상위 스코프에 선언된 변수의 사용이 가능하다.
➡️ 지역 스코프와 전역 스코프가 하나의 스코프 체인으로 연결되어야한다.
3. 함수의 실행을 모두 마치면, 함수 실행 이전의 스코프로 돌아가 남은 코드를 마저 실행한다.
➡️ 현재 실행중인 코드와 이전에 실행하던 코드를 구분하여 관리해야한다.
✨실행 컨텍스트는 렉시컬 환경을 통해 식별자와 스코프를 관리하며, 실행 컨텍스트 스택을 통해 코드의 실행 순서를 관리함으로써 위의 모든 역할을 수행한다.
따라서 모든 코드는 실행 컨텍스트를 통해 실행되고, 관리된다.
한 가지 예시를 보겠다.
const name = "고릴라";
function introduce(name) {
const x = "내 이름은 ";
const y = "입니다.";
console.log(x + name + y);
}
introduce(name);
console.log(name);
위 소스코드가 실행되는 간단한 동작 과정은 다음과 같다.
- 전역 코드 평가
- 전역 코드 실행
- 함수 코드 평가
- 함수 코드 실행
각각의 단계별로 구체적으로 어떻게 이루어지고 있으며, 실행 컨텍스트와 무슨 관계인지 알아보겠다.
1. 전역 코드 평가
코드를 평가하는 데도 몇 가지 단계로 나뉘어 구성된다.
먼저, 전역 실행 컨텍스트를 생성하고, 실행 컨텍스트 스택에 push한다.
실행 중인 실행 컨텍스트는 실행 컨텍스트 스택에 push하여 코드의 실행 순서를 기억할 수 있도록 한다.
그 다음, 전역 렉시컬 환경을 생성하고, 전역 실행 컨텍스트와 바인딩한다.
렉시컬 환경에는 환경 레코드와 외부 렉시컬 환경에 대한 참조 2개의 컴포넌트를 갖는다.
환경 레코드는 객체 환경 레코드와 선언적 환경 레코드로 나뉜다.
(렉시컬 환경에 대해 다루면 길어질 것 같은 늒낌으로,, 자세한 내용은 다음 글에 작성하겠습니다.., 😂)
그 다음에는 선언문을 실행하여 변수와 함수를 등록한다.
전역 변수 name과 전역 함수 intoduce의 선언문이 실행문만을 실행하여 전역 실행 컨텍스트가 관리하는 스코프에 등록한다. 이 때 var로 선언한 변수는 객체 환경레코드에, let과 const로 선언한 변수는 선언적 환경 레코드에 들어간다.
환경 레코드에 저장될 때는 변수의 이름을 key, 변수와 바인딩된 값을 value로 하는 객체 형태로 저장된다.
➡️ {
변수이름: 변수와 바인딩된 값
}
const로 선언된 변수 name은 선언적 환경 레코드에, 함수 선언문으로 선언된 함수 foo는 객체 환경 레코드에 객체 형태로 저장되었다.
전역범위에 선언된 변수나 함수가 모두 등록되었으면, this 바인딩이 이루어진다. 그 후에는 외부 렉시컬 환경에 대한 참조를 결정한다.
외부 렉시컬 환경에 대한 참조라는 것은 결국 현재 실행되고 있는 소스코드의 상위 스코프를 가리키는 단계이다.
지금 실행되고 있는 렉시컬 환경이 만약 전역 렉시컬 환경이라면, 상위 스코프는 존재하지 않는다.
따라서 외부 렉시컬 환경에 대한 참조는 null이 할당된다.
2. 전역 코드 실행
이제 다시 소스코드를 실행할 준비를 마쳤다. 이제 소스코드로 돌아가 위에서 부터 한 줄 한 줄, 변수의 할당문과 함수 호출문을 실행한다.
변수에 값을 할당하거나, 함수의 호출을 실행하기 위해서는 이 변수가 등록된 변수인지, 이 함수가 등록된 함수인지를 확인한 후, 사용해야한다. 따라서 현재 실행중인 컨텍스트부터 등록된 식별자인지 확인한다.
이 때 만약 현재 실행중인 실행 컨텍스트에 식별자가 존재하지 않으면 외부 렉시컬에 대한 참조가 가리키는 즉, 상위 스코프로 이동하여, 식별자를 찾는다. 또 없으면 상위 스코프로 또 이동하고, 이동하고, 이동한다.. 상위스코프가 없는 전역 스코프까지 이동하게 된다.
➡️ 이것을 스코프 체인이라고 한다.
만약에 전역 스코프까지 이동했는데 내가 사용하려는 식별자가 등록되어있지 않았다면 이런 경우 참조 에러가 발생하게 되는 것이다.
const a = 0;
function func1(a) {
const b = 1;
console.log( a + b );
func2(a,b)
function func2(a,b){
const c = 1;
console.log(a + b + c + d );
}
}
func1(a);
3. 전역 함수 평가
런타임 시점에 코드(첫 번째 예) 가 실행되다가 함수 실행문을 만났다. 그 즉시 해당 함수 introduce의 내부로 제어권이 이동한다. 이동한 후에는 위와 같은 동작이 발생한다.
- 함수 실행 컨텍스트 생성
- 함수 렉시컬 환경 생성
함수 실행 컨텍스트가 생성되면 실행 컨텍스트에 동일하게 push 한다.
실행 컨텍스트는 다음과 같이 현재 실행되고 있는 것을 상위 컨텍스트 위에 쌓으면서 코드의 실행순서를 기억한다.
실행 컨텍스트 스택이 push를 하고나면, 전역 때와 동일하게 함수 렉시컬 환경을 생성하고, 함수 실행 컨텍스트와 바인딩한 후에, 환경 레코드( 그 안에는 전역 환경 레코드, 선언적 환경 레코드) 와 외부 렉시컬 환경에 대한 참조 컴포넌트를 생성한다.
함수의 내부 코드에서 선언문만을 실행하여 환경 레코드에 변수를 등록한다.
4. 전역 함수 실행
함수의 내부 코드 실행의 경우에도 동일하다. 위에서부터 한 줄 한 줄 실행문을 수행한다.
등록된 x와 y 변수에 값을 할당해준다.
할당이 끝난 후 코드의 흐름은 밑으로 넘어가서 console.log( x + name + y)을 실행한다.
console 식별자의 경우 전역 객체에 존재하는 메소드이다. 지금 실행되고 있는 실행 컨텍스트는 introduce 함수의 실행 컨텍스트이다. 따라서 스코프 체인을 통해 상위 스코프로 하나하나 넘어가서 전역 스코프에 도달하여 검색이 가능하다.
console 식별자를 찾은 후에는 console.log() 안에 있는 표현식 (x+ name+ y) 을 평가하여 값을 구해야한다.
변수에 바인딩 된 값을 찾기 위해서 현재 실행중인 렉시컬 환경을 통해 식별자를 검색해본다.
x,y 변수의 경우 현재 실행중인 렉시컬 환경 (함수 introduce 렉시컬 환경) 안에 등록되어있지만, name의 경우
현재 렉시컬 환경에서 등록되어있지 않은 변수이다.
따라서 해당 변수의 값을 찾기 위해 상위 스코프로 넘어간다. 상위 스코프는 전역 렉시컬 환경이다.
전역 렉시컬 환경에서 검색하여 찾은 name의 값을 이용해 x+name+y을 계산한다.
이와 같은 단계를 거쳐 콘솔창에 "내 이름은 고릴라입니다" 가 출력된다.
💡결론
실행 컨텍스트란 많은 개념을 담고 있지만 짧게 요약해보자면 다음과 같다.
( 사실 면접 질문에 대한 답변을 작성하기 위해 요약한 글이다. 🙄)
실행 컨텍스트는 코드를 실행할 때 필요한 환경을 제공하고, 결과를 관리하는 공간이다. 실행 컨텍스트와 바인딩되는 렉스컬 환경을 통해 스코프를 구분하여 변수명과 바인딩되는 값을 key, value 관계의 객체로 정리하고, 관리할 수 있으며, 스코프 체인을 형성함으로써 상위 스코프에 등록된 변수를 참조 가능하게 한다. 또한, 실행 컨텍스트 스택을 사용하여 현재 실행 중인 코드와 이전에 실행한 코드를 구분하여 정리함으로써 코드의 실행 흐름을 체계적으로 관리해준다.
따라서 모든 코드는 실행 컨텍스트를 통해 실행되고, 관리된다.
실행 컨텍스트에 대해 '모던 자바스크립트 딥다이브' 로 공부를 진행했다.
해당 파트를 공부하면서 실행 컨텍스트의 개념 뿐만 아니라 전체적인 소스 코드가 실행되는 과정이나, 또는 호이스팅이 되는 이유 그리고 var는 선언단계와 초기화 단계가 동시에 이루어지지만, let과 const의 경우 두 단계가 분리되어 이루어지는 이유 등, 우리가 면접 질문으로 많이 받는 중요한 파트와 깊은 관련이 있는 주제였다. 그래서 이 개념만 공부하는데 정말 많은 시간을 할애한 것 같다.
많은 개념을 담고 있는만큼 머릿속에 정리하기가 쉽지 않았는데, 블로그를 통해 글로 작성해봄으로써 최종적으로 정리가 된 느낌이다. 👯♀️
📑참고자료
- 모던 자바스크립트 Deep Dive, 이웅모
'JavaScript' 카테고리의 다른 글
[JavaScript] CSR, SSR, SSG (0) | 2024.03.28 |
---|---|
[JavaScript] Array.from()을 어떻게 활용할까 (0) | 2024.03.06 |
[JavaScript] 동기(Synchronous)와 비동기(Asynchronous) (1) | 2024.02.02 |
[JavaScript] HTTP (1) | 2024.01.28 |
[JavaScript] Event 객체 (1) | 2024.01.27 |