모던 프론트엔드 프레임워크인 리액트나, 뷰에서 대부분의 요청을 비동기로 진행해야 한다는 이야기를 자주 듣곤 한다. 그렇다면 왜? 굳이 비동기 프로그래밍을 해야 할까? 에 대한 궁금증이 생겼다.
따라서 비동기가 뭔지, 왜 비동기 프로그래밍을 하게 됐는지, 어떻게 하는지, 어떤 장점이 있는지를 흐름에 따라서 간단하게 정리해보고자 한다.
동기와 비동기
동기는 어떤 작업을 요청했을 때, 그 작업이 종료될 때까지 기다린 후 다음 작업을 수행하는 방식이다.
반면 비동기는 어떤 작업을 요청했을 때 그 작업이 종료될 때까지 기다리지 않고, 다른 작업을 먼저 수행하고 있다가 요청했던 작업이 종료되면 그에 대한 추가 작업을 진행하는 방식이다.
예시를 들어 설명해보자면 동기는 음식을 주문한 후 그 요리가 완료되어 내게 서빙될 때까지 기다리는 것이다. 나는 음식을 받을 것을 요청했기 때문에 주문한 음식이 준비될 때까지 다른 일은 할 수 없다. 순차적으로 처리되기 때문이다.
하지만 비동기는 음식을 받을 것을 주문한 후, 요리의 완료'만'을 기다리지 않아도 된다. 그동안 이메일을 보내거나 친구와 이야기를 하는 등 다른 작업을 처리할 수 있다. 작업이 독립적으로, 병렬적으로 처리되기 때문이다.
싱글 스레드
그렇다면 원래의 주제인 모던 프론트엔드에서 비동기 프로그래밍을 해야 하는 근본적인 원인은 무엇일까? 바로, 자바스크립트는 싱글 스레드(Single Thread) 언어이기 때문이다.
여기서 스레드는 CPU에서 실행되는 가장 작은 단위로, 프로세스 내에서 독립적으로 실행되는 작업 단위를 나타내는 말이기도 하다. 자바스크립트는 이러한 가장 작은 작업 단위가 하나뿐이다.
작업 단위, 즉 스레드가 하나뿐이면 한 번에 한 가지 일밖에 처리하지 못하기 때문에 비효율적이라는 생각이 들 수 있다. 하지만 오히려 이러한 싱글 스레드 접근 방식은 브라우저라는 용량이 제한된 공간 위에서 실행되어야 하는 자바스크립트를 간단하게 유지하고 스레드 관리에 따른 복잡성을 줄이는데 도움을 준다.
또한 순차적으로 한 번에 한 가지 일밖에 실행하지 못하는 단점을 극복하고 병렬로 작업을 처리하는 비동기 작업을 수행하기 위해서 이벤트 루프라는 것을 사용하는데, 이러한 이벤트 루프를 사용해 비동기 프로그래밍을 구현해 여러 작업을 효율적으로 다룰 수 있다.
이벤트 루프
이벤트 루프란 자바스크립트와 같은 단일 스레드 환경에서 비동기 작업을 관리하고 실행하는 핵심 메커니즘 중 하나로, 코드 실행을 순차적으로 진행하면서 비동기 작업을 계획하고 처리하는 작업을 한다.
즉, 순차적으로 실행할 수 밖에 없는 싱글 스레드의 단점을 극복하고 비동기 프로그래밍을 진행하기 위해 자바스크립트는 이벤트 루프를 사용하는데, 이러한 이벤트 루프는 결국 브라우저의 동작 타이밍을 제어하는 관리자의 역할을 맡는다고 볼 수 있다.
비동기 프로그래밍에서 해당 소스코드의 평가와 실행을 제외한 모든 처리는 자바스크립트가 구동되는 브라우저, 또는 서버의 node.js가 담당하게 된다. 예를 들어 어떤 함수를 실행하는 것은 자바스크립트가 담당하지만, 함수의 스케줄링 등 그 작업들을 전담하여 처리하는 것은 브라우저나 node.js가 진행한다.
비동기 작업을 마치면 그 함수들이 우측 하단의 콜스택이라는 공간에 쌓여 대기하게 되는데, 이 비동기 함수들을 적절한 시점에 실행시키는 관리자가 바로 이벤트 루프이다. 그럼 적절한 시점인지, 어떤걸 먼저 실행할지 이벤트 루프는 무엇을 보고 판단하게 될까? 바로 마이크로 태스크 큐와, 태스크 큐이다.
마이크로 태스크 큐, 태스크 큐
이 두가지는 이벤트 루프와 관련된 개념으로, 비동기 작업을 처리하고 실행 스택에 추가되길 기다리는 작업을 보관하게 된다. 일반적으로 마이크로 태스크 큐에 있는 마이크로 태스크가 먼저 처리되고 그 후 태스크 큐의 작업 실행이 실행 스택에 추가되게 된다.
마이크로 태스크 큐는 비동기 작업 중 우선 순위가 높은 프로미스나 async/await 등의 작업이 마이크로 태스크 큐에 들어간다. 그 외 일반적인, 대부분의 비동기 작업은 태스크 큐에 보관되는데, setTimeout, setInterval, 이벤트 콜백 등이 있다.
싱글스레드와 이벤트 루프의 상관 관계
이렇게 비동기 작업의 중요도에 따라 보관 공간을 나누고, 어떻게 실행되는지를 위의 그림으로 살펴보자.
- 먼저 이벤트 루프는, 콜스택이 비어있는지 지속적으로 확인한다.
- 콜스택이 비어있으면 우선순위가 더 높은 마이크로 태스크 큐를 확인하고, 오래된 큐를 꺼내서 콜스택으로 전달한다.
- 이를 마이크로 태스크 큐가 텅 비어있을 때까지 수행한다.
- 마이크로 태스크가 처리된 직후, 렌더링 작업이 필요하면 렌더링이 일어난다.
- 그 후 태스크 큐를 확인한다.
- 이전과 마찬가지로 태스크 큐에서 가장 오래된 태스크를 하나 꺼내 콜스택에 전달하면서 이벤트 루프는 비동기 작업을 처리하게 된다.
즉 자바스크립트는 원래 싱글 스레드여서 순차적으로, 동기적으로 작동할 수 밖에 없는데 브라우저에서 제공하는 이벤트 루프를 활용해 비동기적으로 여러 작업을 수행할 수 있다.
비동기 프로그래밍의 구현
이렇게 비동기 프로그래밍을 구현하기 위해서는 코드를 구현하며 자주 사용했을 콜백 함수, 프로미스, async/await을 사용하면 된다. 각각의 내용은 따로 정리할 예정이기 때문에 본 글에서는 이러한 비동기 키워드/기능들을 사용한다 까지만 정리하려 한다.
모던 프론트엔드에서 비동기가 중요한 이유
그래서 이렇게 싱글 스레드의 단점을 극복하면서까지 비동기 프로그래밍을 해야 하는 이유는,
첫째, 비동기 작업 시 필요한 작업만 수행할 수 있어 자원을 효율적으로 사용할 수 있다.
둘째, 마찬가지로 필요한 데이터만 비동기적으로 로드해오면 페이지를 로딩하는 시간이 단축된다.
셋째, 비동기로 요청할 경우 promise를 사용하는데, 프로미스가 제공해주는 기능인 에러 핸들링을 통해 서버 요청 중 오류가 발생해도 애플리케이션을 계속 실행할 수 있다. 따라서 사용자에게 오류 메세지를 표시하거나 대안 작업을 제시할 수 있다.
넷째, 이렇게 부분적으로, 상대적으로 빠르게 로드해올 수 있는 비동기 작업은 웹 애플리케이션의 반응성을 향상시키기 때문에 사용자는 화면의 멈춤 없이 계속 상호작용할 수 있다. 따라서 사용자가 웹 애플리케이션을 더 부드럽게 사용할 수 있고 사용자 경험이 향상되므로, 병렬 처리인 비동기를 활용해 비동기 프로그래밍을 진행한다고 볼 수 있다.
결론적으로, 비동기 프로그래밍은 단순히 작업을 더 효율적으로 처리하는 것 이상의 의미를 지닌다. 싱글 스레드 환경에서 자바스크립트가 비동기 작업을 통해 여러 작업을 동시에 처리할 수 있도록 해줌으로써, 웹 애플리케이션의 성능과 사용자 경험을 크게 향상시킨다. 비동기 방식으로 자원을 효율적으로 활용하고, 빠르게 데이터를 로딩하며, 오류를 처리하는 과정에서 애플리케이션이 중단되지 않고 지속적으로 실행될 수 있게 도와준다.
따라서, 모던 프론트엔드 개발에서 비동기 프로그래밍은 필수적이며, 이를 잘 활용하면 더 빠르고 반응성이 뛰어난 애플리케이션을 구축할 수 있다. 다양한 비동기 처리 기법인 콜백 함수, 프로미스, async/await 등을 적절히 사용하여 복잡한 작업들을 효율적으로 관리하고, 사용자에게 더 나은 경험을 제공하는 데 중점을 두는 것이 중요하다!
'Front-End > JavaScript' 카테고리의 다른 글
[JavaScript] 자바스크립트의 얕은 복사(shallow copy)와 깊은 복사(deep copy) (1) | 2025.03.05 |
---|---|
[JavaScript] 객체 내부에서의 getter, setter (0) | 2024.08.19 |
[JavaScript] 객체의 속성에 접근하는 점 표기법(마침표 표기법)과 대괄호 표기법 (ft. 좋은 코드) (0) | 2024.08.15 |
[JavaScript] 특수 언어가 있는 문자열 배열을 정렬하는 방법, localeCompare() (3) | 2024.07.23 |
[JavaScript] JSON 알아보기 (0) | 2023.05.01 |