EventLoop 분석하기!

얼마 전까지 비동기가 뭔지도 몰랐지만

오늘 공부를 통해서 제가 지금 공부하고 있는 Nodejs라는 비동기 서버사이드 프레임워크에 대해

좀더 알아가볼까 합니다.

사실 지금까지 Nodejs에서 비동기 비동기 수 없이 들었지만 그게 어떤 의미인지 몰랐었는데

친구들에게 비동기가 좋다고 떠들고 다녔던 제가 부끄럽네요 ㅎㅎ

앞으로 공부도 더 하고 반성도 해야겠습니다.

제가 이벤트 루프를 공부하는데 참고한 자료들입니다.


dovelet 문제
도블렛 문제 - complete_graph

이벤트 루프
https://youtu.be/8aGhZQkoFbQ
https://youtu.be/QyUFheng6J0

Loupe
제가 만든 예제를 Loupe에서 돌려본 겁니다.
Loupe example


처음에 이벤트 루프가 어떻게 생겨먹은지도 몰랐고, API와 큐의 존재에 대해서도 완전히

문외한이었던 제가 Loupe가 눈으로 직접 보여준 이벤트 루프의 작동 과정은 신세계였습니다.

그래서 Loupe가 흘러가는 과정을 통해 이벤트 루프를 설명 하려고 합니다. ㅎ

아래의 코드는 도블렛 문제를 푸는 과정을 이벤트 루프로 만들어 풀 수 있지 않을까라는

생각에 만들어 본 코드입니다.

 var input = 4;

 var sum = 0;

 [1,2,3].forEach(function(i)
 {
    function calculate(i){
        setTimeout(function(){
            sum += i;
            if(i === input -1) console.log(sum);
        },1000);
    }
    calculate(i);
 });

코드 자체는 단순합니다. 1부터 3까지 더하는 것이죠.

그래도 순차적으로 1, 2, 3 순으로 sum에 더한다는 점에서 간단한 이벤트 루프를 시연해보기에는

나쁘지 않아보였습니다.

위의 Loupe 하이퍼링크를 클릭하시면 제가 만든 코드의 이벤트 루프가 어떻게 흘러가는지 보실 수 있습니다.

제가 할 일은 Loupe의 스크린샷으로 차례차례 짚고 넘어가며 이벤트 루프가 어떻게 실행되어지는지

확인해 보는 것이죠.

위의 코드의 이벤트 루프는 아래 사진과 같이 시작됩니다.

먼저 forEach 메서드와 calculate, 그리고 setTimeout 을 스택에 쌓습니다.

여기까지는 다른 언어에서도 볼 수있는 특징이죠.

바로 여기서부터 이벤트 루프의 시작인데

setTimeout이 자신의 실행을 web Api로 넘겨버리고 스택에서 빠져버립니다.

그뒤에 web Api가 실행을 끝낼때까지 기다리지 않고(non-blocking의 의미가 여기 있군요)

나머지 코드의 실행이 이루어집니다. 스택이 빠지거나 쌓이는 것이 setTimeout이 실행되는 것과는

상관없이 이루어지는 것이죠.

이동안 web Api는 1초를 기다린다음

setTimeout에 설정했던 콜백을 큐로 보냅니다.

아직 큐에 쌓인 익명함수는 실행되지 않습니다.

그 이유는 자바스크립트 자체는 단일 스레드 방식이라 한번에 한가지밖에 일을 처리하지

못하기 때문입니다.

그래서 스택에서 모든 일이 끝날때까지 기다렸다가 스택이 비었을 때!!!

그제서야 익명함수가 스택에 추가가 됩니다. 이 부분은 제 눈에 매우 중요해 보였습니다.

위의 사진처럼 스택이 완전히 비었을 때서야 쌓여있던 익명함수가 큐에서 빠져 스택에 쌓입니다.

그리고 스택에서 콜백 익명함수가 호출해야 할 모든 스택을 쌓은뒤 스택이 익명함수 자기 자신까지

모두 빠지고 나서야 다음 익명함수가 호출이 됩니다.

반복적으로 익명함수가 모두 실행이 된뒤에 다음 익명 함수가 차례대로 실행이 됩니다.


처음 비동기라는 단어를 배운 것은 생활코딩에서였는데 그때 이런 표현을 썼던 것 같습니다.

표를 받고 끝나길 기다린다. 이게 무슨뜻인지 몰랐는데 이 표를 받고 기다리는게 스택이 아니라

큐에 들어있는 콜백들이라는 점을 이번 공부를 통해 이제서야 깨달을 수 있었습니다.

그리고 제가 앞으로 Nodejs에서 비동기 프로그래밍을 배워가며 주의할점에 대해서도 느낀 것 같습니다.

한 단어로 표현하자면 바로 타이밍인데, 코딩을 할때 스택이 모두 사라지고 그다음 콜백 큐에서

콜백 함수들이 실행이 될텐데 잘못해서 스택이 끝나기 전에 콜백큐에서 익명함수가 실행이

되야한다면 이 코드는 문제를 일으킬 것입니다.

따라서 콜백 큐가 쌓여있을 때, 어떤 타이밍에 스택을 모두 비우게 될 것인지가가

비동기 프로그램의 버그를 찾거나 방지하는 중요한 키가 될것이라 생각합니다.

또한 콜백은 어떤 코드가 들어가야 하는가에 대한 힌트를 얻은 것 같습니다.

제가 처음 제가 사용한 도블렛 문제를 풀려고 했을 때,

재귀적으로 풀려고 했습니다.

function foo(){
    setTimeout(function(){
        foo();
    },0}
}

대충 이런식이였죠. 이런 코드는 언제 스택이 소멸되고 언제 큐에서 빼와 스택에 쌓게 하는지
매우 헷갈리게 합니다.

간단하게 보자면 콜백에는 내부에 재귀를 사용할 지언정 외부에 재귀 함수를 놓지 말자고

볼 수 있을지도 모르지만

근본적으로 보자면 콜백 함수의 실행은 비교적 독립적으로 실행이 될 수 있어야 하지 않을까 싶습니다.

기본적으로 스택은 코드를 보거나 프로파일러를 보면 어느정도 눈에라도 보이지만,

web Api와 큐는 눈에 보이지 않기때문에 더 문제를 찾기가 어려운 것 같습니다.

그렇기 때문에 큐의 실행을 최대한 독립시켜 스택과의 타이밍 문제를 줄이는 것이

버그를 막는 방법이 아닐까 합니다.

이걸로 이벤트 루프의 리뷰를 마침니다.

P.S 저는 개발자가 아니라서 실재 개발 상황에서 이벤트 루프 실행시 어떤 문제가 발생하는지 알지 못합니다.
제가 생각하는 부분에서 잘못된 점이 있다면 지적부탁드려요!

1개의 좋아요

감사합니다. 수정했어요!