자바스크립트는 어떻게 멈추지 않고 일할까? 이벤트 루프와 비동기 프로그래밍의 비밀"
1. 서론: 자바스크립트의 역설, "싱글 스레드인데 빠르다?"
자바스크립트는 한 번에 하나의 일만 처리할 수 있는 싱글 스레드(Single Thread) 언어입니다. 하지만 우리가 사용하는 웹사이트는 네트워크 요청을 보내면서도 애니메이션이 돌아가고, 사용자 클릭에 즉각 반응합니다. 어떻게 한 개의 스레드로 이 모든 '동시성'을 구현하는 걸까요? 그 비밀은 자바스크립트 엔진 자체가 아닌, 브라우저가 제공하는 이벤트 루프(Event Loop) 아키텍처에 있습니다.
2. 자바스크립트 런타임의 구조
자바스크립트가 실행되는 환경(브라우저나 Node.js)은 여러 구성 요소가 협력하는 구조입니다.
Call Stack: 작성한 코드가 순차적으로 쌓이고 실행되는 곳입니다.
Web APIs: 브라우저에서 제공하는 별도의 스레드입니다.
setTimeout,fetch, DOM 이벤트 등이 여기서 처리됩니다.Task Queue (Callback Queue): Web API에서 처리가 끝난 콜백 함수들이 대기하는 공간입니다.
Event Loop: Call Stack이 비어있는지 끊임없이 감시하며, 비어있을 때 Queue에 대기 중인 함수를 Stack으로 옮겨줍니다.
3. 태스크의 우선순위: 매크로 vs 마이크로
모든 비동기 작업이 똑같은 대기 줄에 서는 것은 아닙니다. 여기서 자바스크립트의 실행 순서가 결정됩니다.
Microtask Queue (우선순위 높음):
Promise.then,MutationObserver,process.nextTick등이 들어갑니다.Macrotask Queue (우선순위 낮음):
setTimeout,setInterval,setImmediate등이 들어갑니다.
중요한 점: 이벤트 루프는 매크로 태스크를 하나 처리하기 전에, 반드시 마이크로 태스크 큐에 쌓인 모든 작업을 먼저 비웁니다. 이 순서를 모르면 코드의 실행 결과를 예측할 수 없습니다.
4. 실무 경험 사례: "순서가 뒤바뀐 데이터, 범인은 비동기 우선순위였다"
[나의 개발 경험: Promise와 setTimeout 사이에서 길을 잃다]
예전에 실시간 채팅 기능을 구현할 때의 일입니다. 사용자가 메시지를 보내면 화면에 즉시 추가하고, 아주 짧은 지연(0초) 후 스크롤을 하단으로 내리기 위해
setTimeout(() => scrollToBottom(), 0)을 사용했습니다. 그런데 가끔 메시지가 추가되기 전에 스크롤 로직이 먼저 실행되어 스크롤이 끝까지 내려가지 않는 버그가 발생했습니다.원인을 분석해 보니, 메시지 데이터를 처리하는 로직은 Promise(마이크로 태스크) 기반이었고, 스크롤 로직은 setTimeout(매크로 태스크) 기반이었습니다. 이론적으로 마이크로 태스크가 먼저 실행되어야 했지만, 제 로직 체인 어딘가에서 순서가 꼬여있었던 것이죠.
결국 저는 모든 로직을
async/await와Promise체인으로 통일하여 마이크로 태스크 큐 내에서 순차적으로 실행되도록 강제했고, 버그를 완벽히 해결했습니다. 이 경험을 통해 '0초 뒤 실행'이 결코 '즉시 실행'을 의미하지 않는다는 것을 뼈저리게 배웠습니다.
5. 결론: 효율적인 자바스크립트 작성을 위하여
이벤트 루프를 이해하는 것은 단순히 기술 면접을 대비하는 것이 아닙니다. 브라우저의 메인 스레드를 방해하지 않고(Non-blocking), 사용자에게 부드러운 경험을 제공하는 코드를 짜기 위한 필수 지식입니다. 무거운 연산은 어떻게 분산할지, 비동기 처리는 어떤 순서로 일어날지 고민하는 습관이 여러분을 실력 있는 개발자로 만들어줄 것입니다.
댓글
댓글 쓰기