메리 크리스마스 🎄
크리스마스인데도 포스팅을 하는 이유는 백엔드파트 리더님과 대화중에 JS 이벤트루프를 설명해보라고 하셨는데
어버버대다가 제대로 설명을 못해서 당황했었다..
리더님은 면접을 보러 들어가면 주니어, 시니어 상관없이 JS 이벤트 루프에 대해서 설명해보라고 질문을 던진다고 하셨다.
그만큼 JS에서 가장 기본적이고 비동기 동작원리를 알려면 무조건 알아야하고 설명을 할 수 있어야 한다.
그래서..! 내일 출근하기전에 제대로 알고 가서 설명을 드리려 포스팅을 해본다.
Event Loop
시작하기에 앞서 간단히 JS에 대해 알아보자.
JavaScript는 싱글 스레드 프로그래밍 언어로서, 한 번에 하나의 작업만 처리할 수 있다.
싱글 스레드인 자바스크립트의 작업을 멀티 스레드로 돌려 작업을 동시에 처리시키게 하던가,
JavaScript가 웹 애플리케이션에서 비동기적인 작업을 수행할 수 있는 이유는 무엇일까 ?
바로 '이벤트 루프(Event Loop)' 이다.
간단히 표현하자면 브라우저의 동작 타이밍을 제어하는 관리자라고 보면 된다.
이 포스팅에서는 JavaScript의 핵심인 이벤트 루프의 원리와 작동 방식을 한번 알아보자.
이벤트 루프의 원리와 구성 요소
사진속 이벤트 루프의 구성 요소를 살펴보자.
- Heap
동적으로 할당된 메모리를 관리하는 영역이다.
객체와 같은 구조체는 이곳에 저장되며, 메모리 관리는 '가비지 컬렉션'에 의해 자동으로 수행된다.
- Call Stack
함수의 호출을 기록하는 자료구조이다.
JavaScript 코드가 실행되면, 함수 호출은 스택에 push 된다.
각 함수가 실행을 마치고 반환될 때, 해당 함수는 스택에서 pop 되어 나간다.
JavaScript는 싱글 스레드 언어이기 때문에, 한 번에 하나의 함수만 처리할 수 있으며 이 처리 과정이 Call Stack에서 일어난다.
- Web APIs
브라우저가 제공하는 비동기적인 기능들을 말한다.
이는 setTimeout, XMLHttpRequest, fetch, DOM 이벤트 등을 포함한다.
JavaScript 엔진이 아니라 브라우저에서 제공되고 실행된다.
이를 통해 JavaScript는 싱글 스레드임에도 불구하고 비블로킹 방식으로 다양한 작업을 동시에 처리할 수 있다.
- Callback Queue
비동기 작업(Web API로부터의 콜백, 사용자 이벤트 등)의 결과를 받는 콜백 함수들이 대기하는 곳이다.
이벤트 루프가 이 큐를 모니터링하며, Call Stack이 비어있을 때 콜백을 Call Stack으로 이동시킨다.
- Event Loop
Call Stack, Web APIs, Callback Queue 간의 작업 흐름을 조정한다.
이 루프는 계속해서 Call Stack을 확인하며, Stack이 비어있고,
콜백이 Callback Queue에 있을 때, 해당 콜백을 Call Stack으로 옮겨 실행하게 됩니다.
싱글 스레드인 JavaScript가 비동기 작업을 수행할 수 있게 해주며, 프로그램이 멈추지 않고 계속해서 실행될 수 있도록 한다.
- Event Table
발생한 이벤트와 그에 해당하는 콜백 함수를 임시로 저장하는 자료구조이다.
예를 들어, addEventListener를 사용하여 이벤트 리스너를 등록하면, 해당 이벤트와 콜백 함수가 Event Table에 등록된다.
이벤트가 발생하면, Event Table은 해당 콜백을 Callback Queue로 이동시킨다.
두 가지의 Callback Queue
Callback Queue에는 (Task)Macrotask queue와 (Job)Microtask queue 두 가지 종류가 있다.
두가지 큐는 비슷하지만 다양한 종류의 작업에 적합한 우선 순위와 실행 타이밍을 제공하기 위해서 나눠진것이다.
Callback Queue는 해당 두가지 큐를 묶어 총칭하는 개념이라고 보면 된다.
각각의 큐는 무슨 작업을 처리 하는지 알아보자.
- (Task) Macrotask queue
이벤트 루프가 관리하는 큐 중 하나로, 일련의 macrotask를 포함한다.
Macrotask는 큐에 들어온 순서대로 처리된다.
한 Macrotask의 처리가 완료되면 다음 Macrotask로 넘어간다.
Macrotask 예시: setTimeout, setInterval, setImmediate (Node.js의 경우), I/O 작업, UI 렌더링 요청 등
- (Job) Microtask queue
ES6/ES2015 에서 소개된 Job Queue는 주로 Promise를 사용할 경우 Job Queue를 사용하게 된다.
promise를 사용할 때 callback 함수 역할을 하는 .then 을 사용하게 되며,
이런 thenable한 함수들은 Job Queue에 추가된다.
Macrotask가 처리된 후, 다음 Macrotask로 넘어가기 전에, 이벤트 루프는 Microtask Queue에 있는 모든 작업을 처리한다.
이는 Microtask가 Macrotask 사이에 빠르게 처리되도록 보장한다.
Microtask Queue는 비어있을 때까지 Microtask를 연속적으로 처리한다.
즉, 하나의 Microtask 처리 중에 새로운 Microtask가 생성되면, 그것도 즉시 처리됩니다.
Microtasks 예시: Promise 처리 (then, catch, finally 콜백), MutationObserver, process.nextTick (Node.js의 경우)
이렇게 두개의 큐에 대해서 알아봤는데 요약해보자면
Job의 우선순위가 task의 우선순위보다 높다.
Job의 높은 우선 순위는 빠른 반응성을 필요로 하는 작업을 적시에 처리할 수 있게 도와줍니다.
반면, task는 더 큰 작업 단위를 관리하여, 애플리케이션의 흐름을 조절하고 성능을 최적화합니다.
우선순위와 처리 방식을 이해하는 것은 비동기 프로그래밍 패턴을 효과적으로 설계하고 최적화하는데 중요하다.
예제 코드와 작동 방식
console.log('Start!');
setTimeout(() => {
console.log('Timeout!');
}, 0);
Promise.resolve('Promise!').then(res => console.log(res));
console.log('End!');
이제 개념을 흝어봤으니 직접 코드를 통한 작동방식을 살펴보자.
1. console.log('Start!')가 Call Stack에 쌓인 후 "start!" 출력
2. setTimeout이 Call Stack에 적재되고 실행되면, 그 안의 콜백 함수가 Web API로 옮겨지고 타이머가 작동한다.
3. 타이머가 종료되면 setTimeout의 콜백 함수는 (Task)MacroTask Queue에 적재된다.
4. Promise 코드가 Call Stack에 적재 되어 실행되고, then 핸들러의 콜백 함수가 (Job)MicroTask Queue에 적재된다.
5. console.log('End!') 코드가 실행되고 콘솔창에 "End!"를 출력된다.
6. 모든 메인 스레드의 자바스크립트 코드가 실행이되어 더이상 Call Stack엔 실행할 스택이 없어 비워지게 된다.
7. 그러면 이벤트 핸들러가 이를 감지하여, Callback Queue에 남아있는 콜백 함수들을 빼와 Call Stack에 적재하게 된다.
8. 이때 2종류의 큐 중 (Job)MicroTask Queue에 남아있는 콜백이 우선적으로 처리된다.
9. (Job)MicroTask Queue가 비어지면, 이제 (Task)MacroTask Queue에 있는 콜백 함수를 Call Stack에 적재시켜 실행한다.
이것이 이벤트 루프다 !
마지막으로 이벤트 루프에 중요한 특징을 알아보자.
- Non-blocking
Event Loop는 I/O 작업이나 타이머 등을 비동기적으로 처리할 수 있게 하여,
메인 스레드가 그 동안 다른 작업을 계속 수행할 수 있도록 합니다.
- Concurrency & Threading
JavaScript는 싱글 스레드 기반이지만, Event Loop와 Callback Queue를 통해 동시성을 가질 수 있다.
이는 JavaScript가 멀티 스레드 언어처럼 작동할 수 있게 하며, 특히 웹 애플리케이션에서 비동기적인 작업 처리에 매우 유용하다.
- Event-Driven Programming
JavaScript는 이벤트 기반 프로그래밍을 지원한다.
사용자 인터랙션, I/O 작업 완료 등의 이벤트에 대응하여 비동기적으로 코드를 실행할 수 있다.
JS 이벤트 루프 시각화
오늘 알아본 이벤트 루프를 직접 코드를 쓰고 조작하여,
애니메이션으로 볼 수 있는 시각화 사이트가 있어 소개해본다.
이것저것 실험해보며 브라우저 내부에서 비동기 동작이 어떠한 순서로 일어나는지 알아보자.
참조:
https://www.youtube.com/watch?v=8aGhZQkoFbQ
https://engineering.linecorp.com/ko/blog/dont-block-the-event-loop
https://wiki.yowu.dev/ko/dev/Nodejs/event-loop
'Programming Language > Javascript' 카테고리의 다른 글
[Javascript] 클로저_코어 자바스크립트 (0) | 2023.09.29 |
---|---|
[Javascript] 콜백 함수_코어 자바스크립트 (0) | 2023.09.04 |
[Javascript] this_코어 자바스크립트 (0) | 2023.08.22 |
[Javascript] console.dir()로 객체의 모든 depth 구조 출력 (0) | 2023.08.15 |
[Javascript] new Set() 배열에서 쉽게 중복값 제거하기 (0) | 2023.08.14 |