이벤트 루프와 비동기 통신의 이해

강연주·2024년 10월 28일

📚 TIL

목록 보기
80/186

이벤트 루프와 비동기 통신의 이해

자바스크립트는 싱글 스레드에서 작동한다. 기본적으로 자바스크립트는 한 번에 하나의 작업만을, 동기 방식으로만 처리할 수 있다는 뜻이다. 동기(synchronous)란, 직렬 방식으로 작업을 처리하는 것을 의미하며, 이 요청이 시작된 이후에는 무조건 응답을 받은 이후에야 비로소 다른 작업을 처리할 수 있다. 그동안 다른 모든 작업은 대기한다. 이러한 방식은 개발자에게 직관적으로 다가오지만, 한 번에 다양한 많은 작업을 처리할 수 없다. 반대의 의미인 비동기(asynchronous)란, 직렬 방식이 아니라 병렬 방식으로 작업을 처리하는 것을 의미한다. 요청을 시작한 후 응답이 오건 말건 상관없이 다음 작업이 이루어지며, 따라서 한 번에 여러 작업이 실행될 수 있다.


자바스크립트는 분명히 싱글 스레드에서 동기 방식으로 작동한다.
그러나 이러한 싱글 스레드 기반의 자바스크립트에서도 많은 양의 비동기 작업이 이루어지고 있다. 모던 웹 애플리케이션에서는 사용자에게 많은 양의 정보를 다양한 방식으로 제공하기 위해 많은 것이 비동기로 작동한다. 구체적인 예를 보자. 사용자가 검색어를 입력해 검색을 위한 네트워크 요청이 발생하는 순간에도, 사용자는 다른 작업을 처리할 수 있다. 이는 동기식의 자바스크립트 방식에서는 불가능한 시나리오처럼 보인다. 분명 자바스크립트는 싱글 스레드의 동기식으로 작동하므로, 검색의 결과를 받기 전까지 아무런 작업도 하지 못하는 것이 자연스러운 듯하다. 그러나 우리는 웹페이지에서 다양한 비동기 작업을 수행하고 있다.


리액트는 또 어떤가? 과거 렌더링 스택을 비우는 방식으로 구현됐던 동기식의 렌더링이, 16버전에 접어들며 비동기식으로 작동하는 방법도 소개됐다. 이처럼 리액트에도 비동기식으로 작동하는 작업이 존재한다.


자바스크립트 환경에서 이런 것들이 어떻게 가능한지 알려면, 비동기 작업의 처리 방식과 비동기 처리를 도와주는 이벤트 루프를 비롯한 다양한 개념을 알아야 한다. 이러한 이해를 통해, 자바스크립트에서 어떻게 여러 가지 요청을 동시에 처리하는지, 요청받은 태스크에 대한 우선순위는 어떠한지, 또 주의할 점은 무엇인지 파악하고 사용자 경험을 향상시킬 수 있을 것이다.


🪝 싱글 스레드 자바스크립트

자바스크립트는 싱글 스레드 언어라는 말을 들어봤을 것이다. 과거에는 프로그램을 실행하는 단위가 오직 프로세스뿐이었다. 프로세스(process)란, 프로그램을 구동해 프로그램의 상태가 메모리상에서 실행되는 작업 단위를 의미한다. 즉, 하나의 프로그램 실행은 하나의 프로세스를 가지고 그 프로세스 내부에서 모든 작업이 처리되는 것이다.

그러나 소프트웨어가 점차 복잡해지면서, 하나의 프로그램에서 동시에 여러 개의 복잡한 작업을 수행할 필요성이 대두됐다. 하지만 하나의 프로그램에는 하나의 프로세스만이 할당되므로 이러한 작업을 수행하기가 어려웠고, 그래서 탄생한 더 작은 실행 단위가 바로 스레드(thread)다. 하나의 프로세스에서는 여러 개의 스레드를 만들 수 있고, 스레드끼리는 메모리를 공유할 수 있어 여러 가지 작업을 동시에 수행할 수 있다. 이에 따라 프로세스 내부에서 여러 개의 스레드를 활용하면서 동시 다발적인 작업을 처리할 수 있게 된 것이다.


자바스크립트는 왜 싱글 스레드로 설계됐을까?

먼저, 멀티 스레드는 여러 가지 이점이 있지만 내부적으로 처리가 복잡하다는 단점이 있다. 스레드는 하나의 프로세스에서 동시에 서로 같은 자원에 접근할 수 있는데, 동시에 여러 작업을 수행하다 보면 같은 자원에 대해 여러 번 수정하는 등, 동시성 문제가 발생할 수 있어 이에 대한 처리가 필요하다. 또한 각각 격리돼 있는 프로세스와는 다르게, 하나의 스레드가 문제가 생기면 같은 자원을 공유하는 다른 스레드에도 동시에 문제가 일어날 수 있다.

최초의 자바스크립트는 브라우저에서 HTML을 그리는 데 한정적인 도움을 주는 보조적인 역할로 만들어졌다. 1995년 자바스크립트가 등장했을 당시에는 멀티 스레드에 대한 개념이 대중화되지 않았다. 최초의 자바스크립트는 단순히 버튼 위에 이미지를 띄우거나, 간단한 경고창을 띄우거나, 폼을 처리하는 등 아주 기초적인 수준에서만 제한적으로 사용됐다. 즉, 설계 당시에는 약 30년 뒤인 현재처럼 자바스크립트가 웹페이지에서 벌어지는 온갖 다양한 일을 처리해야 할 것이라고 예상하지 못했을 것이다. 게다가, 자바스크립트로 DOM을 조작하는 경우에, 자바스크립트가 멀티 스레딩을 지원해 동시에 여러 스레드가 DOM을 조작할 수 있다면, 메모리 공유로 인해 동시에 같은 자원에 접근하는 타이밍 이슈가 생길 수 있고 이는 브라우저의 DOM 표시에 문제를 야기할 수 있다.


자바스크립트가 싱글 스레드라는 것은, 자바스크립트 코드의 실행이 하나의 스레드에서 순차적으로 이루어진다는 뜻이다. 하나의 스레드에서 순차적으로 이루어진다는 것은 코드를 한 줄 한 줄 실행한다는 것이고, 궁극적으로 하나의 작업이 끝나기 전까지는 뒤이은 작업이 실행되지 않음을 의미한다. C 언어나 다른 메이저 프로그래밍 언어에서는 스레드에서 실행 중인 함수를 시스템이 임의로 멈추고, 다른 스레드의 코드를 먼저 실행할 수 있지만, 자바스크립트에는 그런 기능이 존재하지 않는다. (Node.js에서 새롭게 추가된 Worker나, 브라우저에서 제공하는 WebWorker를 활용하면 동시에 여러 작업을 처리할 수 있지만 이 두 기능은 최근에 나온 것으로 논외로 한다.)


하나의 코드가 실행되는 데 시간이 오래 걸리면, 뒤 코드가 실행되지 않는 자바스크립트의 특징을 'Run-to-completion'이라고 한다.
자바스크립트 개발자가 동시성을 고민할 필요가 없다는 부분에서 아주 큰 장점이 되지만, 역으로 때에 따라서 웹페이지에서는 단점이 될 수 있다. 하나의 작업이 끝나기 전까지는 다른 작업이 실행되지 않으므로, 어떠한 작업이 오래 걸린다면 사용자에게 웹페이지가 멈춘 듯한 느낌을 줄 수 있다. 결론적으로 Run-to-completion, 즉 자바스크립트의 모든 코드는 '동기식'으로 한 번에 하나씩 순차적으로 처리된다.


비동기란, 동기식과 다르게 요청한 즉시 결과가 주어지지 않을 수도 있고, 따라서 응답이 언제 올 수도 알 수 없지만, 여러 작업을 동시에 수행할 수 있다는 장점이 있다.

🖥️ console.log(1)

setTimeout(() => {
  console.log(2)
}, 0)

setTimeout(() => {
  console.log(3)
}, 100)

console.log(4)

위 코드를 실행하면 콘솔에는 1, 4, 2, 3의 순서로 값이 출력된다.
1, 2, (0.1초 후에) 3, 4로 출력되지 않고 비동기식으로 코드가 처리된 것이다. 이는 '이벤트 루프' 개념에 대한 이해로 설명할 수 있다.


🪝 이벤트 루프란?

🏄 이벤트 루프


( 출처 : 모던 리액트 Deep Dive, 김용찬, 위키북스 )

profile
아무튼, 개발자

0개의 댓글