JavaScript 실행 환경

contability·2024년 4월 15일
0
post-thumbnail

JavaScript는 싱글 스레드 언어이지만 비동기 처리가 가능하다.
이는 JavaScript 엔진과 브라우저(또는 Node.js) 런타임 환경의 협력으로 이루어진다.

🔄 전체 흐름 간단 요약

1단계: 코드 실행

  • 함수가 호출되면 → 콜 스택에 쌓여서 순서대로 실행

2단계: 비동기 작업 발견

  • 콜 스택을 처리하는 중에 setTimeout, fetch 같은 비동기 코드를 만나면 → Web API로 넘겨서 백그라운드에서 처리

3단계: 작업 완료 대기

  • Web API에서 작업이 끝나면 → 콜백 함수를 대기줄(콜백 큐)에 넣고 순서를 기다림

4단계: 실행 준비

  • 이벤트 루프가 계속 확인하다가 콜 스택이 비어있으면 → 대기줄의 콜백을 콜 스택으로 옮겨서 실행

핵심: JavaScript는 한 번에 하나씩만 처리하지만, 시간이 걸리는 작업은 다른 곳에서 처리하고 나중에 결과만 받아온다!

1. Call Stack (콜 스택)

정의

콜 스택은 JavaScript 엔진이 함수 호출을 추적하는 LIFO(Last In, First Out) 구조의 자료구조다.

동작 방식

  • 함수가 호출되면 콜 스택의 맨 위에 푸시(push) 된다
  • 함수 실행이 완료되면 콜 스택에서 팝(pop) 된다
  • JavaScript는 한 번에 하나의 함수만 실행할 수 있다 (싱글 스레드)
  • 콜 스택이 비어있을 때만 새로운 작업을 받을 수 있다

예시

function first() {
    console.log('첫 번째');
    second();
    console.log('첫 번째 종료');
}

function second() {
    console.log('두 번째');
    third();
    console.log('두 번째 종료');
}

function third() {
    console.log('세 번째');
}

first();

콜 스택 변화:

1. [first] ← first() 호출
2. [first, second] ← second() 호출  
3. [first, second, third] ← third() 호출
4. [first, second] ← third() 완료, 팝
5. [first] ← second() 완료, 팝
6. [] ← first() 완료, 팝

Stack Overflow

콜 스택이 허용된 크기를 초과하면 "Maximum call stack size exceeded" 에러가 발생한다.

function infiniteRecursion() {
    infiniteRecursion(); // Stack Overflow 발생
}

2. Web API

정의

Web API는 브라우저가 제공하는 비동기 작업 처리 환경이다. JavaScript 엔진과는 별개의 독립적인 스레드에서 동작한다.

주요 Web API들

  • Timer API: setTimeout, setInterval
  • DOM API: addEventListener, DOM 조작
  • Network API: fetch, XMLHttpRequest
  • Storage API: localStorage, sessionStorage
  • Geolocation API: navigator.geolocation

동작 방식

  1. JavaScript에서 비동기 함수 호출
  2. 해당 작업을 Web API로 위임
  3. Web API에서 백그라운드로 작업 수행
  4. 작업 완료 시 콜백 함수를 Task Queue에 추가

예시

console.log('1');

setTimeout(() => {
    console.log('2 - 타이머 완료');
}, 1000);

console.log('3');

처리 과정:
1. console.log('1') → 콜 스택에서 즉시 실행
2. setTimeout → Web API의 Timer로 위임 (1초 타이머 시작)
3. console.log('3') → 콜 스택에서 즉시 실행
4. Web API에서 콜백을 콜백 큐에 추가

3. Callback Queue (콜백 큐)

정의

콜백 큐는 Web API에서 완료된 비동기 작업의 콜백 함수들이 대기하는 FIFO(First In, First Out) 구조의 큐다.

종류

JavaScript 환경에는 여러 종류의 큐가 있다:

Macro Task Queue (매크로 태스크 큐)

  • setTimeout, setInterval
  • setImmediate (Node.js)
  • I/O 작업
  • UI 렌더링

Micro Task Queue (마이크로 태스크 큐)

  • Promise.then/catch/finally
  • queueMicrotask()
  • MutationObserver

우선순위

Micro Task Queue > Macro Task Queue

마이크로 태스크 큐가 완전히 비워져야 매크로 태스크 큐의 작업이 실행된다.

예시

console.log('1');

setTimeout(() => console.log('2 - setTimeout'), 0);

Promise.resolve().then(() => console.log('3 - Promise'));

console.log('4');

실행 순서:

1
4
3 - Promise
2 - setTimeout

4. Event Loop (이벤트 루프)

정의

Event Loop는 콜 스택과 콜백 큐들을 지속적으로 모니터링하며, 콜 스택이 비어있을 때 콜백 큐의 작업을 콜 스택으로 이동시키는 메커니즘이다.

동작 알고리즘

while (true) {
    if (콜 스택이 비어있음) {
        if (마이크로 태스크 큐에 작업이 있음) {
            마이크로 태스크를 콜 스택으로 이동
        } else if (매크로 태스크 큐에 작업이 있음) {
            매크로 태스크를 콜 스택으로 이동
        }
    }
}

주요 특징

  • Non-blocking: 콜 스택이 비어있을 때만 작업 이동
  • 우선순위 처리: 마이크로 태스크를 먼저 처리
  • 연속 실행: 한 번에 하나의 태스크만 처리

전체적인 실행 흐름

복합 예시

console.log('시작');

setTimeout(() => {
    console.log('setTimeout 1');
    Promise.resolve().then(() => console.log('Promise 3'));
}, 0);

Promise.resolve().then(() => {
    console.log('Promise 1');
    setTimeout(() => console.log('setTimeout 2'), 0);
});

Promise.resolve().then(() => console.log('Promise 2'));

console.log('끝');

실행 과정 분석

1단계 - 동기 코드 실행:

콜 스택: [console.log('시작')]
출력: '시작'

2단계 - setTimeout 처리:

setTimeout → Web API로 위임
콜 스택: []
Web API: [Timer(0ms, callback1)]

3단계 - 첫 번째 Promise:

Promise.resolve().then() → 마이크로 태스크 큐에 추가
마이크로 태스크 큐: [callback2]

4단계 - 두 번째 Promise:

Promise.resolve().then() → 마이크로 태스크 큐에 추가  
마이크로 태스크 큐: [callback2, callback3]

5단계 - 동기 코드 완료:

콜 스택: [console.log('끝')]
출력: '끝'
콜 스택: [] ← 비워짐

6단계 - 이벤트 루프 동작:

1. 마이크로 태스크 우선 처리
   출력: 'Promise 1'
   새로운 setTimeout → Web API로 위임
   
2. 다음 마이크로 태스크 처리  
   출력: 'Promise 2'
   
3. 마이크로 태스크 큐 비워짐, 매크로 태스크 처리
   출력: 'setTimeout 1'
   새로운 Promise → 마이크로 태스크 큐에 추가
   
4. 마이크로 태스크 다시 우선 처리
   출력: 'Promise 3'
   
5. 마지막 매크로 태스크 처리
   출력: 'setTimeout 2'

최종 출력:

시작
끝
Promise 1
Promise 2
setTimeout 1
Promise 3
setTimeout 2

핵심 원칙 정리

1. 실행 순서

동기 코드 → 마이크로 태스크 → 매크로 태스크

2. 블로킹 방지

콜 스택이 비어있을 때만 콜백 큐의 작업이 실행된다.
무거운 콜 스택 작업이 있으면 콜백 큐에 쌓여있는 비동기 콜백들이 지연된다.

3. 마이크로 태스크의 우선순위

마이크로 태스크 큐가 완전히 비워질 때까지 매크로 태스크는 대기한다.

4. Web API의 독립성

비동기 작업은 메인 스레드를 블로킹하지 않고 백그라운드에서 처리된다.

이러한 메커니즘을 통해 JavaScript는 싱글 스레드임에도 불구하고 효율적인 비동기 처리가 가능하다.

0개의 댓글