⚡리액트 개발을 위해 꼭 알아야할 자바스크립트 - 1.5 이벤트 루프와 비동기 통신의 이해
1.5.1 싱글 스레드 자바스크립트
🌚자바 스크립트
- 한번에 하나의 작업만 동기 방식으로 처리할 수 있음(싱글 스레드)
- 하나의 코드가 실행하는데 오래걸리면 뒤의 코드가 실행되지 않는 것 > "Run-to-completion"
- 자바스크립트는 비동기 처리가 가능한 것을 이해하려면 이벤트 루프를 알아야 함
- 동기 : 직렬방식
- 비동기 : 병렬방식
자바스크립트는 싱글스레드지만, 브라우저(혹은 Node.js)가 제공하는 이벤트 루프(Event Loop)와 백그라운드 API(Web API, Thread Pool) 덕분에 비동기 처리가 가능하다.
1.5.2 이벤트 루프란?
- 자바 스크립트 런타임 외부에서 자바스크립트의 비동기 실행을 돕기 위해 만들어진 장치
💡호출스택과 이벤트 루프
- 호출스택(call stack) : 자바스크립트에서 수행해야 할 코드나 함수를 순차적으로 담아두는 스택
🌚 foo를 호출하고, 내부에서 bar, baz를 순차적으로 호출하는 구조
function bar() {
console.log('bar')
}
function baz() {
console.log('baz')
}
function foo() {
console.log('foo')
setTimeout(bar(), 0)
baz()
}
foo()
- foo() 가 호출 스택에 먼저 들어간다
- foo() 내부에 console.log가 존재하므로 호출 스택에 들어간다
- 2의 실행이 완료된 이후에 다음 코드로 넘어간다
- bar()가 호출 스택에 들어간다
- bar() 내부에 console.log가 존재하므로 호출스택에 들어간다
- 5의 실행이 완료된 이후에 다음 코드로 들어간다
- 더 이상 bar()에 남은 것이 없으므로 호출 스택에서 제거된다
- baz()가 호출 스택에 들어간다
- baz() 내부에 console.log가 존재하므로 호출 스택에 들어간다
- 9의 실행이 완료된 이후에 다음 코드로 넘어간다
- 더 이상 baz()에 남은 것이 없으므로 호출 스택에서 제거된다
- 더 이상 foo()에 남은 것이 없으므로 호출 스택에서 제거된다
- 이제 호출 스택이 완전히 비워준다
이벤트 루프(Event Loop) 의 역할은 호출 스택(Call Stack) 이 비어 있는지 확인하는 것이다.
호출 스택이 비어 있으면 태스크 큐(Task Queue) 에서 대기 중인 콜백(작업)을 꺼내와 실행한다.
"코드 실행"과 "호출 스택 확인"은 모두 하나의 싱글 스레드 위에서 순차적으로 일어난다.
🌚비동기 작업은 어떻게 실행될까?
function bar() {
console.log('bar');
}
function baz() {
console.log('baz');
}
function foo() {
console.log('foo');
setTimeout(bar, 0);
baz();
}
foo();
- foo() 가 호출 스택에 먼저 들어간다
- foo() 내부에 console.log가 존재하므로 호출 스택에 들어간다
- 2의 실행이 완료된 이후에 다음 코드로 넘어간다
- setTimeout(bar(), 0)가 호출 스택에 들어간다
- 4번에 대해 타이머 이벤트가 실행되며 태스크 큐로 들어가고, 그 대신 바로 스택에서 제거된다
- baz()가 호출 스택에 들어간다
- baz() 내부에 console.log가 존재하므로 호출 스택에 들어간다
- 7의 실행이 완료된 이후에 다음 코드로 넘어간다
- 더 이상 baz()에 남은 것이 없으므로 호출 스택에서 제거된다
- 더 이상 foo()에 남은 것이 없으므로 호출 스택에서 제거된다
- 이제 호출 스택이 완전히 비워졌다
- 이벤트 루프가 호출 스택이 비워져 있다는 것을 확인했다. 그리고 태스트 큐를 확인하니 4번에 들어갔던 내용이 있어 bar()를 호출 스택에 들여보낸다
- bar() 내부에 console.log가 존재하므로 호출 스택에 들어간다
- 13의 실행이 끝나고, 다음 코드로 넘어간다
- 더 이상 bar()에 남은 것이 없으므로 호출 스택에서 제거된다
위 코드에서 setTimeout(() ⇒ {}, 0) 이 정확히 0초 뒤에 실행된다는 것을 보장하지 못한다.
🌕이벤트 루프
- 태스크 큐를 한 개 이상 가지고 있음( ※ 태스크 큐: 실행해야 할 태스크의 집합을 의미 )
- 자료구조의 queue가 아니고 set 형태인데, 그 이유는 선택된 큐 중에서 실행 가능한 가장 오래된 태스크를 가져와야 하기 때문
- 태스크 큐에서 의미하는 '실행해야 할 태스크'라는 것은 비동기 함수의 콜백 함수나 이벤트 핸들러 등을 의미
🌕이벤트 루프의 역할
- 호출 스택에 실행 중인 코드가 있는지, 태스크 큐에 대기 중인 함수가 있는지 반복해서 확인하는 것
- 호출 스택이 비었다면 태스크 큐에 대기 중인 작업이 있는지 확인하고, 이 작업을 실행 가능한 오래 된 것부터 순차적으로 꺼내와서 실행
1.5.3 태스크 큐의 마이크로 태스크 큐
- 마이크로 태스크 큐는 기존 태스크 큐보다 우선권을 갖는다. (promise)
- setTimeout과 setInterval은 Promise보다 늦게 실행된다.
functioni foo() {
console.log('foo')
}
functioni bar() {
console.log('bar')
}
functioni baz() {
console.log('baz')
}
setTimeout(foo, 0)
Promise.resolve().then(bar).then(baz)
예제코드를 실행하면 bar, baz, foo 순으로 실행된다. 확실히 Promise가 우선권이 있다.
각 태스크에 들어가는 대표적인 작업은 아래와 같다.
- 태스크 큐: setTimeout, setInterval, setImmediate
- 마이크로 태스크 큐: process.nextTick, Promises, queueMicroTask, MutationObserver
💊렌더링은 언제 실행될까?
태스크 큐를 실행하기전 마이크로 태스크 큐를 실행하고, 마이크로 태스크 큐를 실행한 뒤에 렌더링이 일어난다.
각 마이크로 태스크 큐 작업이 끝날 때마다 한 번씩 렌더링할 기회를 얻게 된다.
1.5.4 정리
자바스크립트 코드를 실행하는 것 자체는 싱글 스레드로 이루어져서 비동기를 처리하기 어렵지만 자바스크립트 코드를 실행하는 것 이외에 태스크 큐, 이벤트 루프, 마이크로 태스크 큐, 브라우저/Node.js API 등이 적절한 생태계를 이루고 있기 때문에 싱글 스레드로는 불가능한 비동기 이벤트 처리가 가능해진 것이다!
'Study > 모던 리액트 Deep Dive' 카테고리의 다른 글
| 모던 리액트 Deep Dive - 타입 스크립트 (1) | 2025.09.23 |
|---|---|
| 모던 리액트 Deep Dive - 자주 사용되는 자바스크립트 문법 (0) | 2025.09.23 |
| 모던 리액트 Deep Dive - 클로저 (1) | 2025.09.15 |
| 모던 리액트 Deep Dive - 클래스 (0) | 2025.09.15 |
| 모던 리액트 Deep Dive - 함수 (0) | 2025.09.15 |
