[ASAC 06] Javascript (2) - Single Thread와 비동기 처리

flavor_blue·2024년 8월 20일

JS 개념 정리

목록 보기
2/3

Thread

컴퓨터에서 Thread는 실행되는 작업의 최소 단위를 말합니다. 즉, 하나의 작업을 실행하면 하나의 Thread 위에 올려서 작업이 진행 되야 한다는 걸 의미하는데, 컴퓨터에서는 이를 여러개 생성하여 여러 작업을 처리하는 소위 멀티 쓰레딩 (Multi-Threading)을 진행합니다. 덕분에 우리는 이것 저것을 한꺼번에 실행하여 즐길 수 있죠.

Single Thread, Synchronous

그러나 웹에서 동작하는 Javascript 언어는 Single Thread 언어입니다. 그 말인 즉, 하나의 행위가 끝나야 다음 행위를 할 수 있다는 것을 의미합니다. 이를 동기(Synchronous) 라고도 표현 합니다.

좋은 예로 alert("Hello Wolrd!"); 함수가 있습니다. 해당 함수를 실행하면, 팝업창에서 확인을 누르기 전 까지 다른 코드의 기능이 동작하지 않게 됩니다.

허나 웹 페이지는 해야 할 일이 너무 많습니다. 이미지 파일도 불러와야 하고, 목록 조회도 해야 하고, 로그인 처리도 해야하고 등등등... 하나의 작업을 처리하고 다음 작업을 진행하는 동기 형태의 프로그래밍은 작업이 너무 오래 걸리고, 응답이 늦어지는 경우에는 전체적인 성능 저하 및 사용자 경험에 영향을 줄 수 있게 됩니다.

비동기 처리, Asynchronous

그래서 Javascript는 다중 처리를 하기 위해 비동기 처리라는 것을 하게 됩니다.

위에서 동기처리는 순차적으로 작업을 수행해 나갈 때, 지금 수행중인 업무가 종료되야 다음 업무가 시작되는 것이라고 했죠. 비동기 처리는 수행해야 하는 여러 업무를 병렬적으로 수행하는 개념 입니다. 즉, A 업무를 진행하다 막히면 그대로 대기하고, B 업무를 수행하는 방식으로 볼 수 있습니다. 그러다 A 업무에 대한 완료 신호를 받으면 그대로 완료 후 작업에 대한 일을 이어나갈 수도 있습니다.

이러한 비동기 처리는, 자바스크립트의 런타임 환경 중 하나인, API를 통해 지원 됩니다. 지난 포스트에서, 자바스크립트의 런타임은 JS Engine + API로 구성이 되있다고 했는데, 바로 이 API를 말합니다. 거기에 웹 브라우저와 서버 별 구동 방식에 따른 차이도 존재하구요.

웹 브라우저 에서의 비동기 처리 지원

웹 브라우저의 API에는 비동기 처리를 지원하는 방식이 2가지가 있습니다. 바로 이벤트 루프를 이용한 방법과 Web Worker API를 사용하는 방법이 있습니다.

이벤트 루프를 이용한 방식

주로 자바스크립트의 입출력 작업을 비동기로 처리하여 메인 쓰레드가 차단되지 않도록 하는 방식 입니다.

처리 과정

  1. 비동기 작업을 요청하면, 브라우저는 해당 작업을 Web API로 던져줍니다. 이때 실행 내용과 실행이 끝나고 처리 될 작업이 담긴 callback 함수를 매개변수로 던져줍니다.
  2. 작업이 완료되면, 그 결과는 이벤트 루프를 통해 메시지 큐에 전달되고, 메인 스레드가 사용 가능한 시점에 해당 작업의 콜백 함수가 실행 됩니다.
    => 이 과정에서 자바스크립트는 싱글 스레드로 동작하지만, 요청한 비동기 작업이 완료 될 때까지 다른 작업을 계속 처리할 수 있습니다.

사용 예시

  • setTimeout : 일정 시간이 지난 후 코드를 실행
  • fetch : 네트워크 요청을 보내고, 응답이 도착하면 처리
  • EventListener : 사용자 인터페이스에서 발생하는 이벤트를 비동기 처리

장점

  • UI 를 Blocking 하지 않고, 효율적으로 작업을 처리할 수 있음.
  • 일반적으로 사용하기 쉽고, 대부분의 I/O 작업에 적합.

단점

  • CPU 집약적인 작업에는 적합하지 않음. (작업이 길어지면 UI가 느려질 수 있음).

Web Worker를 이용한 방식

Web Workder는 자바스크립트의 메인 스레드와 별도로 실행되는 백그라운드 스레드 입니다. 주로 CPU 집약적인 작업을 처리하여 메인 스레드의 부하를 줄이는 데 사용됩니다. 프론트엔드 개발자가 직접 멀티 스레드를 활용하여 개발하고 싶을 때도 사용한다고 하네요.

처리 과정

  • Web Worker는 별도의 스레드에서 작업을 수행하기 때문에, 메인 스레드와 독립적으로 실행됩니다.
  • 메인 스레드와 Web Worker는 메시지 전달 방식으로 통신합니다. 즉, 데이터를 전달하고 그 결과를 받기 위해 메시지를 주고 받습니다. (Pub/Sub Pattern)
  • 비동기적으로 메시지를 주고 받기 때문에, 메인 스레드는 Web Workder의 작업이 완료되기를 기다리지 않고, 다른 작업을 계속 수행할 수 있습니다.

사용 예시

  • 대규모 데이터 처리 : 이미지 처리, 비디오 인코딩, 복잡한 계산 등
  • 병렬 작업 : 여러 Web Worker를 사용하여 병렬 처리가 가능.

장점

  • 메인 스레드와 독립적으로 작업을 수행하기 때문에 UI가 Blocking 되지 않음.
  • 복잡한 연산 처리에 유용하며, 성능 최적화에 도움을 줌.

단점

  • Worker 스레드 간의 데이터 전달 필요시, 직렬화/역직렬화 과정이 필요함. => 오버헤드 발생
  • 메모리 사용량이 증가할 수 있음
  • 단순한 비동기 작업보단 설정이 복잡할 수 있음.

웹 서버에서의 비동기 처리

정확히는 Node.js 에서 사용하는 LIBUV가 웹 서버에서의 비동기 처리 역할을 진행합니다.
Node.js도 Single Thread 기반의 런타임 환경인데, Libuv는 이런 환경에서 비동기적으로 I/O 작업을 처리할 수 있도록 지원합니다.

Node APIs + Libuv

주요 기능

  1. 이벤트 루프
  2. I/O 작업 관리
  3. 스레드 풀(Thread pool)
  4. 비동기 스트림 처리
  5. 파일 시스템 이벤트 처리

처리 방식

  1. Node APIs에서 비동기 처리를 적재합니다.
  2. 네이티브 비동기 작업(예: 네트워크 운영)은 운영체제의 비동기 I/O 기능을 통해 처리 됩니다.
    해당 기능이 없는 작업(예: 일부 파일 시스템 작업)은 'libuv'의 스레드 풀에서 처리 됩니다.
  3. 작업이 완료되면 해당 작업 결과가 이벤트 큐에 추가됩니다. 이벤트 루프는 큐에서 작업을 하나씩 가져와 등록된 콜백 함수를 실행합니다.
  4. 각 작업 완료 후, Node.js의 메인 스레드에서 해당 작업의 콜백 함수가 실행 됩니다. 이 과정은 비동기적으로 처리되기 때문에 메인 스레드가 다른 작업을 Blocking 하지 않습니다.

📑 출처 및 참조
[ASAC] 강의 자료
https://dev.to/bbarbour/if-javascript-is-single-threaded-how-is-it-asynchronous-56gd
https://en.wikipedia.org/wiki/Thread_(computing)
https://inpa.tistory.com/entry/%F0%9F%8C%90-js-async
https://poiemaweb.com/js-async

profile
아무거나 쓰려하지 말고 생각하며 쓰고 싶습니다

0개의 댓글