이벤트 루프란 무엇인가?
23 Apr 2019해당 포스트는 Understanding JS: The Event Loop를 번역하여 작성하였습니다. 잘못된 부분이 있다면 댓글 부탁드립니다.
수 많은 라이브러리, 툴과 같은 것들 때문에 개발이 쉬워졌고, 많은 개발자가 원리에 대한 고민없이 어플리케이션을 만들고 있습니다. 자바스크립트는 이런 행동의 선두주자입니다. 자바스크립트는 복잡하고 널리 퍼진 언어중 하나이고, 많은 개발자가 좋은 툴을 사용하고 언어에 안좋은 점을 우회해서 사용하는 것에 매력을 느끼고 있습니다.
당신도 놀라운 어플리케이션을 이러한 방식으로 빌드할 수 있으며 자바스크립트를 경험해보는 것도 충분히 좋은 경험이 될 수 있습니다. 자바스크립트 생태계는 계속 변화하지만 자바스크립트 원리를 활용하는것은 변하지 않습니다. 그것들을 이해하면 보는 시야가 넓어지고 개발 프로세스를 바라보는 관점을 바꿀 수 있습니다.
이벤트 루프란 무엇인가?
여러분은 자바스크립트가 싱글 스레드 언어라는 것을 들어보셨을 겁니다. 또한 콜스택과 이벤트 큐에 대해서도 들어보셨을 겁니다. 대부분의 사람은 이벤트루프 덕분에 콜백함수와 promise를 사용할 수 있다고 알고 있습니다. 그러나 그것보다 많은 내용이 있습니다. 너무 깊게 들어가지않고 자바스크립트 코드가 어떻게 실행되는지 개괄적으로 살펴보겠습니다.
콜 스택
자바스크립트는 콜스택을 가지고 있습니다. 그 안에는 어떤 함수가 현재 실행중이고 다음에는 어떤 함수가 실행될지에 대한 트랙이 있습니다. 그런데 스택이 무엇일까요? 스택은 배열과 비슷한 데이터 구조이지만 제약이 있습니다. 후입선출 방식만 가능합니다. 다른 예로 들면 접시더미라고 생각하시면 됩니다. 쌓여진 접위 맨위에 접시를 올리고 위에있는 접시부터 꺼내씁니다.
당신이 함수를 실행할 때 그 함수는 콜스택에 추가됩니다. 그리고 만약 그 함수가 또 다른 함수를 호출하면 그 함수는 콜스택의 맨위에 추가됩니다. 콘솔에 에러가 찍혔을 때 에러 실행 경로에 대한 메세지를 확인 할 수 있습니다. 이것은 스택이 그 순간에 무엇을 보고있었는지 확인할 수 있습니다. 그러나 만약에 요청을하고 시간제한을 설정하면 어떻게 될까요? 이론적으로 함수가 실행될때까지 브라우저가 멈추고 콜스택이 계속 진행되어야할까요? 그러나 실제로 이벤트큐와 이벤트테이블때문에 그렇게 발생하지 않습니다.
이벤트 테이블 & 이벤트 큐
타임아웃 함수 혹은 비동기로 코드를 동작을 호출할 때마다 그것은 이벤트 테이블에 추가됩니다. 데이터 테이블은 데이터 구조로 특정 이벤트 후에 특정 함수가 트리거되어야 한다는 것을 알고 있습니다. 이벤트(타임아웃, 클릭, 마우스무브)가 발생하면 신호를 보냅니다. 이벤트 테이블은 함수를 실행하지 않고 콜스택에 함수를 추가하지 않는다는 것을 염두해 두세요. 이벤트 테이블의 목표는 이벤트 트랙을 유지하고 그것을 이벤트 큐에 보내는 것입니다.
이벤트큐는 스택과 비슷한 데이터구조 입니다. 후입선출만 가능합니다. 이것은 이벤트 테이블로부터 함수 호출을 전달 받습니다. 그러면 어떻게 그것을 콜스택으로 보낼까요? 여기서 이벤트 루프가 등장합니다.
이벤트 루프
우리는 이제 악명높은 이벤트 루프에 대해서 알아보겠습니다. 이벤트 루프는 지속적으로 콜스택이 비어있는지 아닌지 체크를 합니다. 시계를 상상해보세요 그리고 매 시간 콜스택을 바라보고 있고 만약 비어있다면 이벤트 큐를 바라보게 됩니다. 콜스택에 옮겨져야하는 무엇이 있다면 옮기고 그렇지않다면 특별한 일이 발생하지 않습니다.
여기에 흥미로운 2개의 케이스가 있습니다. 어떤 코드가 먼저 실행될 것이라고 생각하세요?
setTimeout(() => console.log("first"), 0);
console.log("second");
일부 사람은 셋 타임아웃이 0이기 때문에 즉시 실행될 것이라고 생각합니다. 하지만 “second”가 “first”보다 먼저 출력되는 것을 확인할 수 있습니다. 자바스크립트는 세타임을 아웃을 “먼저 나의 이벤트 테이블에 넣은 다음에 계속해서 실행해야겠다”라고 받아들입니다. 그것은 이벤트 테이블, 이벤트 큐로 옮겨지고 실행되어지기 위해서 이벤트 루프를 기다리게 됩니다.
문제점
또 다른 이벤트 루프의 흥미있는 예시는 재귀입니다. 혹시 스택오브플로우에 대한 에러메세지를 확인해보신적이 있으신가요? 때때로 무한 재귀함수를 사용하거나 큰 수의 재귀적인 호출을 할 때 발생합니다. 재귀적 호출은 셋 타임아웃으로 감싸면 재귀적 호출의 문제를 해결할 수 있습니다.
recursion()(재귀함수라로 가정)을 호출하는 것 대신에 직접 셋타임아웃을 호출할 수 있습니다. setTimeout(() => recursion(), 0) 이로써 스택오브플로우를 방지할 수 있습니다. 왜냐하면 함수가 바로 출되지않고 이벤트 테이블과 이벤트 큐를 거치게되기 때문입니다.