의도: 자바스크립트의 기본 원리와 함께 기술 면접 대표 질문에 대해 준비해왔는지 확인하는 질문
팁
나의 답안
실행 컨텍스트는 자바스크립트 코드가 실행되는 환경 정보 묶음이라고 할 수 있습니다.
함수가 호출될 때마다 새로운 실행 컨텍스트가 생성되고,
그 안에는 변수 환경(Variable Environment), 렉시컬 환경(Lexical Environment), 그리고 this 바인딩 정보가 포함됩니다.
이 실행 컨텍스트들은 스택(Stack) 구조로 관리되어, 가장 최근에 호출된 함수가 먼저 실행되고, 끝나면 스택에서 제거됩니다.그런데 자바스크립트는 싱글 스레드 기반이기 때문에,
네트워크 요청이나 타이머 등 오랜 시간이 걸리는 작업을 모두 한 스레드에서 처리하면 다른 코드가 멈춰버릴 수 있습니다.
이 문제를 해결하기 위해 이벤트 루프(Event Loop)와 태스크 큐(Task Queue)가 동작합니다.이벤트 루프의 역할은 콜 스택이 비는 순간, 대기 중인 콜백 함수를 태스크 큐에서 꺼내 실행시키는 것입니다.
즉, 비동기 작업들은 Web API 영역에서 수행되고,
완료된 콜백이 태스크 큐에 들어가면 이벤트 루프가 스택이 비었을 때 하나씩 처리합니다.
이 덕분에 자바스크립트는 싱글 스레드임에도 불구하고 비동기적으로 동작할 수 있습니다.
주어진 답안 (모범 답안)
우선 이벤트 루프는 그 이름답게 이벤트를 순서대로 돌아가며 처리하는 역할을 합니다.
마치 줄을 서 있는 것처럼 이벤트 루프는 큐에 담긴 이벤트를 하나하나 호출 스택으로 옮깁니다.
물론 실행 중인 컨텍스트가 없어 호출 스택이 비어있을 때만 말입니다.그리고 실행 컨텍스트는 이러한 이벤트가 실행되는 환경을 뜻합니다.
앞서 말한 호출 스택에 이러한 실행 컨텍스트를 쌓아두고 작업이 종료되면 스택에서 빠져나오게 됩니다.이러한 이벤트 루프와 실행 컨텍스트는 싱글 스레드 환경인 자바스크립트에서 비동기 작업을 효과적으로 처리하기 위해 필수적인 메커니즘입니다.
자바스크립트의 이벤트 루프와 실행 컨텍스트는 자바스크립트 엔진의 핵심 동작 방식과 관련된 중요한 개념이다.
이를 이해하면 비동기 코드의 동작 방식과 콜백 처리, async/await, Promise 등이 작동하는 원리를 더 명확히 알 수 있다.
실행 컨텍스트는 자바스크립트 코드가 실행되는 환경이다.
자바스크립트 엔진은 코드를 실행할 때 실행 컨텍스트를 생성하고, 이 컨텍스트가 함수 호출, 변수, 스코프, 그리고 this와 같은 정보를 관리한다.
let, const, var로 선언된 변수들이 저장되는 곳이다.this가 무엇을 가리키는지에 대한 정보가 저장된다.this 바인딩이 설정된다.window 또는 global)를 포함한다.eval() 함수 내부에서 코드 실행 시 생성된다. (잘 사용되지 않음)실행 컨텍스트와 콜 스택의 관계
실행 컨텍스트는 콜 스택(Call Stack)에 쌓인다.
함수가 호출될 때마다 실행 컨텍스트가 생성되고, 실행이 끝나면 해당 컨텍스트는 스택에서 제거된다.
예시
function first() {
console.log("First");
second();
}
function second() {
console.log("Second");
}
first();
- 처음 실행: 전역 컨텍스트 생성
- 코드 실행 시작 시 전역 실행 컨텍스트가 콜 스택에 쌓인다.
first()함수 호출
first()실행 컨텍스트가 콜 스택에 추가된다.console.log("First")가 실행된다.second()함수 호출
second()실행 컨텍스트가 콜 스택에 추가된다.console.log("Second")가 실행된다.second()종료
second()컨텍스트가 콜 스택에서 제거된다.first()종료
first()컨텍스트가 콜 스택에서 제거된다.
First
Secondfunction outer() {
let x = 10;
function inner() {
console.log(x); // 10
}
inner();
}
outer();inner 함수는 outer 함수의 변수(x)에 접근할 수 있다.자바스크립트는 싱글 스레드(Single Thread) 언어이다. 즉, 한 번에 하나의 작업만 실행된다.
하지만, 비동기 작업(예: setTimeout, 네트워크 요청, 파일 읽기 등)을 처리하기 위해 이벤트 루프라는 메커니즘을 사용한다.
이벤트 루프는 콜 스택(Call Stack), 태스크 큐(Task Queue), 그리고 마이크로태스크 큐(Microtask Queue)를 관리하며 자바스크립트 코드의 실행 순서를 결정한다.
push), 실행이 끝나면 제거(pop)된다.setTimeout)이 완료되면, 해당 콜백이 태스크 큐에 들어간다.Promise.then, MutationObserver 등 마이크로태스크는 별도의 우선순위 높은 큐에 저장된다.이벤트 루프 동작 예시
console.log("Start");
setTimeout(() => {
console.log("Timeout");
}, 0);
Promise.resolve().then(() => {
console.log("Promise");
});
console.log("End");
"Start"→console.log("Start")는 바로 실행 (콜 스택에 들어갔다가 바로 제거)"End"→console.log("End")도 즉시 실행Promise.then()→ 마이크로태스크 큐에 들어가므로"Promise"가 먼저 출력setTimeout→ 태스크 큐에 들어가므로"Timeout"이 마지막에 실행
Start
End
Promise
Timeout자바스크립트는 싱글 스레드로 동작하지만, 논블로킹 I/O를 통해 비동기 작업을 처리한다.
이 비동기 처리는 브라우저나 Node.js의 백그라운드 스레드에서 실행된다.
브라우저에서의 비동기 작업 처리
setTimeout(() => console.log("Timeout"), 1000);
fetch("https://api.example.com").then(() => console.log("Fetch done"));
console.log("Sync task");
setTimeout: 브라우저의 타이머 API가 처리한다.fetch: 브라우저의 네트워크 스레드가 요청을 처리한다.console.log(동기 코드): 즉시 실행된다.async/await와 이벤트 루프async/await는 비동기 코드를 동기 코드처럼 보이게 작성할 수 있게 한다.
하지만 내부적으로는 Promise를 사용하며, 결국 마이크로태스크 큐를 활용한다.
async function example() {
console.log("Start");
await new Promise(resolve => setTimeout(resolve, 1000));
console.log("End");
}
example();
console.log("Outside");
"Start"가 출력된다.await으로 인해Promise가 해결될 때까지 기다린다. → 이 시점에 함수 실행이 중단된다."Outside"가 출력된다. → 이벤트 루프가 계속 실행된다.- 1초 후
Promise가 해결되면"End"가 출력된다.
Start
Outside
Endfunction counter() {
let count = 0;
return function increment() {
count++;
console.log(count);
};
}
const inc = counter();
setTimeout(inc, 1000); // 1
setTimeout(inc, 2000); // 2
counter 함수가 종료된 후에도 count 변수를 기억한다.setTimeout이 처리되면서 클로저가 작동한다.