Javascript는 웹 페이지의 동적인 행위를 구현하기 위해서 탄생한 프로그래밍 언어입니다. 그러나 그 기능과 역할은 시간이 지남에 따라 웹 애플리케이션의 복잡한 로직을 처리하는 등 더욱 확장되었습니다. Javascript가 이처럼 다양한 작업을 처리할 수 있는 근본적인 이유 중 하나는 이벤트 루프(Event Loop) 와 비동기 처리(Asynchronous Processing) 메커니즘 덕분입니다.
Javascript와 비동기 처리의 필요성
Javascript는 싱글 스레드 언어입니다. 이는 코드가 한번에 하나의 작업만 수행할 수 있음을 의미합니다. 이러한 환경에서 네트워크의 요청이나 파일 입출력과 같은 시간이 많이 소요되는 작접을 처리할 때, 해당 작업이 완료될 때까지 코드의 실행이 멈추게 됩니다. 이를 해결하기 위해 Javascript는 비동기 처리 패턴을 도입하였습니다.
동기 vs 비동기 개념
동기 작업은 작업 수행 순서가 코드의 작성 순서와 일치하는 방식입니다. 한 작업이 완료되기 전까지는 다음 작업이 시작되지 않습니다.
반면, 비동기 작업은 현재 진행중인 작업의 완료 여부와 상관없이 다음 작업을 바로 시작할 수 있습니다. 이는 특히 네트워크 요청이나 대규모 파일 처리와 같이 오랜 시간이 걸리는 작업을 백그라운드에서 실행할 때 유용합니다.
이벤트 루프(Event Loop)
이벤트 루프는 비동기 작업의 실행 순서를 관리하는 Javascript의 핵심 메커니즘입니다.
주로 Javascript엔진은 호출 스택, 이벤트 큐, 백그라운드 작업 이 세 가지 주요 구성 요소를 사용하여 이벤트 루프를 구현합니다.
호출 스택(Call Stack)
호출 스택은 현재 실행 중인 함수의 기록을 저장하는 구조입니다. 함수가 호출되면 스택의 최상단에 추가되고, 함수의 실행이 완료되면 스택에서 제거됩니다.
이벤트 큐(Event Queue)
이벤트 큐는 비동기 작업의 콜백 함수가 실행을 기다리는 대기열입니다. 이벤트 루프는 호출 스택이 비어 있을 때 이벤트 큐에서 함수를 하나씩 가져와 호출 스택에 넣어 실행합니다.
백그라운드 작업
타이머, I/O 작업, 네트워크 요청 등 비동기 작업은 Javascript 엔진 외부에서 실행됩니다. 이 작업들이 완료되면 그에 해당하는 콜백함수가 이벤트 큐에 추가됩니다.
비동기 작업에 대한 예를 들어보면
let num = 1;
num = 3;
setTimeout(() => {
num = 2;
}, 0);
console.log(num);
해당 코드를 실행하게 되면 실제 콘솔에서는 2가 아닌 3이 출력됩니다.
그 이유가 무엇일까요? 해당 코드의 실행 순서를 알아봅시다.
- let num = 1; 로 num 변수를 1로 초기화 합니다.
- num = 3; 로 num 변수의 값을 3으로 변경합니다.
- setTimeout 함수가 호출되며, 0밀리초 후에 num = 2; 를 실행하기로 예약됩니다. 하지만 이 코드는 즉시 실행되지 않고, 현재 실행 중인 스크립트가 완료된 후에 실행됩니다.
- console.log(num); 가 실행되면서 이 시점에서 num의 값은 3이기 때문에 3을 콘솔에 출력합니다
- 마지막으로 setTimeout 내부의 콜백 함수가 실행됩니다. 그러나 이 시점에서 이미 console.log가 실행되어 값을 출력했기 때문에, 이 변경의 영향을 받지 않습니다.
setTimeout 함수는 0밀리초의 지연 시간을 가지고 있음에도 불구하고, 실제로는 현재 스크립트가 완료된 후에야 그 내부의 콜백 함수가 실행됩니다. 따라서 setTimeout 함수에서 num = 2 를 실행하기로 예약만 받고 console.log(num); 가 실행되어 3이 출력되는 것입니다.
따라서 위의 모습과 같이 setTimeout 함수에 2000밀리초의 지연 시간을 주면 현재 스크립트가 완료가 되어도(3이 출력되어도) 내부의 콜백 함수가 실행되면서 2000밀리초 이후에 종료되는 모습입니다.
동기적 처리와 비동기적 처리의 비교
동기적 처리 방식에서는 만약 setTimeout 대신 시간이 오래 걸리는 작업을 동기적으로 실행했다면, 해당 작업이 완료될 때 까지 다음 줄의 코드 실행이 지연됩니다. 이는 애플리케이션의 반응형을 크게 저하시킬수 있습니다. 반면, 비동기 처리 방식은 이러한 작업을 백그라운드에서 처리하고, 작업의 완료와 상관없이 다음 코드를 계속 실행할 수 있게 해줍니다. 이는 특히 웹 애플리케이션에서 사용자 경험을 크게 향상시키는 중요한 기법이라고 할 수 있습니다.
비동기 패턴의 다른 예시
Javascript는 setTimeout 외에도 비동기 작업을 처리하기 위한 여러 패턴을 제공합니다. 가장 대표적인 것이 프로미스(Promise) 와 async/await입니다. 프로미스는 비동기 작업의 최종 성공 또는 실패를 나타내는 객체이며, async/await는 프로미스를 더욱 쉽게 사용할 수 있도록 하게 해주는 문법입니다.
결론
이벤트 루프와 비동기 처리는 Javascript의 핵심적인 부분이며, 이를 이해하는 것은 모던 웹 개발에서 매우 중요합니다. 이 메커니즘을 통해 비동기 작업을 효율적으로 관리하고, 사용자에게 부드럽고 반응이 빠른 웹 애플리케이션을 제공할 수 있습니다.
추가자료
'Javascript' 카테고리의 다른 글
Javascript에서의 이벤트 캡쳐링, 버블링 및 이벤트 위임 이해하기 (0) | 2024.01.28 |
---|---|
var, let, const 를 중복 선언 허용, 스코프, 호이스팅 관점에서 알아보기 (0) | 2024.01.21 |
얕은복사(Shallow Copy) , 깊은 복사(Deep Copy) (0) | 2024.01.21 |